|
@@@ -63,7 -63,7 +63,7 @@@ runs
|
|
|
63
63
|
|
|
64
64
|
- name: Upload constraints file
|
|
65
65
|
if: inputs.constraints-file-artifact-name != ''
|
|
66
|
--
uses: actions/upload-artifact@
|
|
66
|
++
uses: actions/upload-artifact@v4
|
|
67
67
|
with:
|
|
68
68
|
name: ${{ inputs.constraints-file-artifact-name }}
|
|
69
69
|
path: venv/constraints.txt
|
|
@@@ -115,7 -115,7 +115,7 @@@ jobs
|
|
|
115
115
|
|
|
116
116
|
- name: Publish JUnit results
|
|
117
117
|
if: always()
|
|
118
|
--
uses: actions/upload-artifact@
|
|
118
|
++
uses: actions/upload-artifact@v4
|
|
119
119
|
with:
|
|
120
120
|
name: junit-data
|
|
121
121
|
path: junit-data/*
|
|
@@@ -191,7 -191,7 +191,7 @@@ jobs
|
|
|
191
191
|
sh build_linux_deb-2-installer.sh ${{ matrix.os.arch }}
|
|
192
192
|
|
|
193
193
|
- name: Upload Linux artifacts
|
|
194
|
--
uses: actions/upload-artifact@
|
|
194
|
++
uses: actions/upload-artifact@v4
|
|
195
195
|
with:
|
|
196
196
|
name: chia-installers-linux-deb-${{ matrix.os.arch }}
|
|
197
197
|
path: ${{ github.workspace }}/build_scripts/final_installer/
|
|
@@@ -233,7 -233,7 +233,7 @@@
|
|
|
233
233
|
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
|
|
234
234
|
|
|
235
235
|
- name: Download constraints file
|
|
236
|
--
uses: actions/download-artifact@
|
|
236
|
++
uses: actions/download-artifact@v4
|
|
237
237
|
with:
|
|
238
238
|
name: constraints-file-${{ matrix.os.arch }}
|
|
239
239
|
path: venv
|
|
@@@ -243,7 -243,7 +243,7 @@@
|
|
|
243
243
|
pip install --constraint venv/constraints.txt py3createtorrent
|
|
244
244
|
|
|
245
245
|
- name: Download packages
|
|
246
|
--
uses: actions/download-artifact@
|
|
246
|
++
uses: actions/download-artifact@v4
|
|
247
247
|
with:
|
|
248
248
|
name: chia-installers-linux-deb-${{ matrix.os.arch }}
|
|
249
249
|
path: build_scripts/final_installer/
|
|
@@@ -396,7 -396,7 +396,7 @@@
|
|
|
396
396
|
- uses: Chia-Network/actions/clean-workspace@main
|
|
397
397
|
|
|
398
398
|
- name: Download packages
|
|
399
|
--
uses: actions/download-artifact@
|
|
399
|
++
uses: actions/download-artifact@v4
|
|
400
400
|
id: download
|
|
401
401
|
with:
|
|
402
402
|
name: chia-installers-linux-deb-${{ matrix.arch.artifact-name }}
|
|
@@@ -184,7 -184,7 +184,7 @@@ jobs
|
|
|
184
184
|
bash build_linux_rpm-2-installer.sh amd64
|
|
185
185
|
|
|
186
186
|
- name: Upload fpm-generated rpm spec files
|
|
187
|
--
uses: actions/upload-artifact@
|
|
187
|
++
uses: actions/upload-artifact@v4
|
|
188
188
|
with:
|
|
189
189
|
if-no-files-found: error
|
|
190
190
|
name: spec
|
|
@@@ -193,7 -193,7 +193,7 @@@
|
|
|
193
193
|
build_scripts/dist/gui.spec
|
|
194
194
|
|
|
195
195
|
- name: Upload Linux artifacts
|
|
196
|
--
uses: actions/upload-artifact@
|
|
196
|
++
uses: actions/upload-artifact@v4
|
|
197
197
|
with:
|
|
198
198
|
name: chia-installers-linux-rpm-intel
|
|
199
199
|
path: ${{ github.workspace }}/build_scripts/final_installer/
|
|
@@@ -229,7 -229,7 +229,7 @@@
|
|
|
229
229
|
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
|
|
230
230
|
|
|
231
231
|
- name: Download constraints file
|
|
232
|
--
uses: actions/download-artifact@
|
|
232
|
++
uses: actions/download-artifact@v4
|
|
233
233
|
with:
|
|
234
234
|
name: constraints-file-intel
|
|
235
235
|
path: venv
|
|
@@@ -239,7 -239,7 +239,7 @@@
|
|
|
239
239
|
pip install --constraint venv/constraints.txt py3createtorrent
|
|
240
240
|
|
|
241
241
|
- name: Download packages
|
|
242
|
--
uses: actions/download-artifact@
|
|
242
|
++
uses: actions/download-artifact@v4
|
|
243
243
|
with:
|
|
244
244
|
name: chia-installers-linux-rpm-intel
|
|
245
245
|
path: build_scripts/final_installer/
|
|
@@@ -394,7 -394,7 +394,7 @@@
|
|
|
394
394
|
- uses: Chia-Network/actions/clean-workspace@main
|
|
395
395
|
|
|
396
396
|
- name: Download packages
|
|
397
|
--
uses: actions/download-artifact@
|
|
397
|
++
uses: actions/download-artifact@v4
|
|
398
398
|
id: download
|
|
399
399
|
with:
|
|
400
400
|
name: chia-installers-linux-rpm-intel
|
|
@@@ -230,7 -230,7 +230,7 @@@ jobs
|
|
|
230
230
|
sh build_macos-2-installer.sh
|
|
231
231
|
|
|
232
232
|
- name: Upload MacOS artifacts
|
|
233
|
--
uses: actions/upload-artifact@
|
|
233
|
++
uses: actions/upload-artifact@v4
|
|
234
234
|
with:
|
|
235
235
|
name: chia-installers-macos-dmg-${{ matrix.os.name }}
|
|
236
236
|
path: ${{ github.workspace }}/build_scripts/final_installer/
|
|
@@@ -284,7 -284,7 +284,7 @@@
|
|
|
284
284
|
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
|
|
285
285
|
|
|
286
286
|
- name: Download constraints file
|
|
287
|
--
uses: actions/download-artifact@
|
|
287
|
++
uses: actions/download-artifact@v4
|
|
288
288
|
with:
|
|
289
289
|
name: constraints-file-${{ matrix.os.name }}
|
|
290
290
|
path: venv
|
|
@@@ -294,7 -294,7 +294,7 @@@
|
|
|
294
294
|
pip install --constraint venv/constraints.txt py3createtorrent
|
|
295
295
|
|
|
296
296
|
- name: Download packages
|
|
297
|
--
uses: actions/download-artifact@
|
|
297
|
++
uses: actions/download-artifact@v4
|
|
298
298
|
with:
|
|
299
299
|
name: chia-installers-macos-dmg-${{ matrix.os.name }}
|
|
300
300
|
path: build_scripts/final_installer/
|
|
@@@ -437,7 -437,7 +437,7 @@@
|
|
|
437
437
|
- uses: Chia-Network/actions/clean-workspace@main
|
|
438
438
|
|
|
439
439
|
- name: Download packages
|
|
440
|
--
uses: actions/download-artifact@
|
|
440
|
++
uses: actions/download-artifact@v4
|
|
441
441
|
id: download
|
|
442
442
|
with:
|
|
443
443
|
name: chia-installers-macos-dmg-${{ matrix.arch.artifact-name }}
|
|
@@@ -258,7 -258,7 +258,7 @@@ jobs
|
|
|
258
258
|
.\build_windows-2-installer.ps1
|
|
259
259
|
|
|
260
260
|
- name: Upload Installer to artifacts
|
|
261
|
--
uses: actions/upload-artifact@
|
|
261
|
++
uses: actions/upload-artifact@v4
|
|
262
262
|
with:
|
|
263
263
|
name: chia-installers-windows-exe-intel
|
|
264
264
|
path: ${{ github.workspace }}\chia-blockchain-gui\release-builds\
|
|
@@@ -302,7 -302,7 +302,7 @@@
|
|
|
302
302
|
directories: ${{ steps.create-venv.outputs.activate-venv-directories }}
|
|
303
303
|
|
|
304
304
|
- name: Download constraints file
|
|
305
|
--
uses: actions/download-artifact@
|
|
305
|
++
uses: actions/download-artifact@v4
|
|
306
306
|
with:
|
|
307
307
|
name: constraints-file-intel
|
|
308
308
|
path: venv
|
|
@@@ -312,7 -312,7 +312,7 @@@
|
|
|
312
312
|
pip install --constraint venv/constraints.txt py3createtorrent
|
|
313
313
|
|
|
314
314
|
- name: Download packages
|
|
315
|
--
uses: actions/download-artifact@
|
|
315
|
++
uses: actions/download-artifact@v4
|
|
316
316
|
with:
|
|
317
317
|
name: chia-installers-windows-exe-intel
|
|
318
318
|
path: chia-blockchain-gui/release-builds/
|
|
@@@ -434,7 -434,7 +434,7 @@@
|
|
|
434
434
|
- uses: Chia-Network/actions/clean-workspace@main
|
|
435
435
|
|
|
436
436
|
- name: Download packages
|
|
437
|
--
uses: actions/download-artifact@
|
|
437
|
++
uses: actions/download-artifact@v4
|
|
438
438
|
with:
|
|
439
439
|
name: chia-installers-windows-exe-intel
|
|
440
440
|
path: packages
|
|
@@@ -47,7 -47,7 +47,7 @@@ jobs
|
|
|
47
47
|
|
|
48
48
|
# Initializes the CodeQL tools for scanning.
|
|
49
49
|
- name: Initialize CodeQL
|
|
50
|
--
uses: github/codeql-action/init@
|
|
50
|
++
uses: github/codeql-action/init@v3
|
|
51
51
|
with:
|
|
52
52
|
languages: ${{ matrix.language }}
|
|
53
53
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
@@@ -58,7 -58,7 +58,7 @@@
|
|
|
58
58
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
59
59
|
# If this step fails, then you should remove it and run the build manually (see below)
|
|
60
60
|
- name: Autobuild
|
|
61
|
--
uses: github/codeql-action/autobuild@
|
|
61
|
++
uses: github/codeql-action/autobuild@v3
|
|
62
62
|
|
|
63
63
|
# ℹ️ Command-line programs to run using the OS shell.
|
|
64
64
|
# 📚 https://git.io/JvXDl
|
|
@@@ -72,4 -72,4 +72,4 @@@
|
|
|
72
72
|
# make release
|
|
73
73
|
|
|
74
74
|
- name: Perform CodeQL Analysis
|
|
75
|
--
uses: github/codeql-action/analyze@
|
|
75
|
++
uses: github/codeql-action/analyze@v3
|
|
@@@ -228,9 -228,9 +228,9 @@@ jobs
|
|
|
228
228
|
mv notchia/ chia/
|
|
229
229
|
|
|
230
230
|
- name: Publish JUnit results
|
|
231
|
--
uses: actions/upload-artifact@
|
|
231
|
++
uses: actions/upload-artifact@v4
|
|
232
232
|
with:
|
|
233
|
--
name: junit-data
|
|
233
|
++
name: junit-data-${{ env.JOB_FILE_NAME }}
|
|
234
234
|
path: junit-data/*
|
|
235
235
|
if-no-files-found: error
|
|
236
236
|
|
|
@@@ -243,9 -243,9 +243,9 @@@
|
|
|
243
243
|
coverage report --rcfile=.coveragerc --show-missing
|
|
244
244
|
|
|
245
245
|
- name: Publish coverage data
|
|
246
|
--
uses: actions/upload-artifact@
|
|
246
|
++
uses: actions/upload-artifact@v4
|
|
247
247
|
with:
|
|
248
|
--
name: coverage-data
|
|
248
|
++
name: coverage-data-${{ env.JOB_FILE_NAME }}
|
|
249
249
|
path: coverage-data/*
|
|
250
250
|
if-no-files-found: error
|
|
251
251
|
|
|
@@@ -119,9 -119,9 +119,10 @@@ jobs
|
|
|
119
119
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
120
120
|
|
|
121
121
|
- name: Download Results
|
|
122
|
--
uses: actions/download-artifact@
|
|
122
|
++
uses: actions/download-artifact@v4
|
|
123
123
|
with:
|
|
124
|
--
|
|
124
|
++
merge-multiple: true
|
|
125
|
++
pattern: junit-data-*
|
|
125
126
|
path: junit-data
|
|
126
127
|
|
|
127
128
|
- name: Format JUnit data and prepare results
|
|
@@@ -135,7 -135,7 +136,7 @@@
|
|
|
135
136
|
ls junit-results/*.xml | xargs --max-procs=10 --replace={} yq eval '.testsuites.testsuite |= sort_by(.+@name) | .testsuites.testsuite[].testcase |= sort_by(.+@classname, .+@name)' --inplace {}
|
|
136
137
|
|
|
137
138
|
- name: Publish formatted JUnit data
|
|
138
|
--
uses: actions/upload-artifact@
|
|
139
|
++
uses: actions/upload-artifact@v4
|
|
139
140
|
with:
|
|
140
141
|
name: junit-data
|
|
141
142
|
path: junit-data/*
|
|
@@@ -143,16 -143,16 +144,17 @@@
|
|
|
143
144
|
|
|
144
145
|
- name: Publish JUnit results
|
|
145
146
|
if: always()
|
|
146
|
--
uses: actions/upload-artifact@
|
|
147
|
++
uses: actions/upload-artifact@v4
|
|
147
148
|
with:
|
|
148
149
|
name: junit-results
|
|
149
150
|
path: junit-results/*
|
|
150
151
|
if-no-files-found: error
|
|
151
152
|
|
|
152
153
|
- name: Download Coverage
|
|
153
|
--
uses: actions/download-artifact@
|
|
154
|
++
uses: actions/download-artifact@v4
|
|
154
155
|
with:
|
|
155
|
--
|
|
156
|
++
merge-multiple: true
|
|
157
|
++
pattern: coverage-data-*
|
|
156
158
|
path: coverage-data
|
|
157
159
|
|
|
158
160
|
- name: Set up ${{ matrix.python.name }}
|
|
@@@ -280,7 -280,7 +282,7 @@@
|
|
|
280
282
|
|
|
281
283
|
- name: Publish coverage reports
|
|
282
284
|
if: always()
|
|
283
|
--
uses: actions/upload-artifact@
|
|
285
|
++
uses: actions/upload-artifact@v4
|
|
284
286
|
with:
|
|
285
287
|
name: coverage-reports
|
|
286
288
|
path: coverage-reports/*
|
|
@@@ -196,7 -196,7 +196,7 @@@ jobs
|
|
|
196
196
|
python -m build --sdist --outdir dist .
|
|
197
197
|
|
|
198
198
|
- name: Upload artifacts
|
|
199
|
--
uses: actions/upload-artifact@
|
|
199
|
++
uses: actions/upload-artifact@v4
|
|
200
200
|
with:
|
|
201
201
|
name: dist
|
|
202
202
|
path: ./dist
|
|
@@@ -6,6 -6,6 +6,20 @@@ The format is based on [Keep a Changelo
|
|
|
6
6
|
and this project does not yet adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
7
7
|
for setuptools_scm/PEP 440 reasons.
|
|
8
8
|
|
|
9
|
++
## 2.1.4 Chia blockchain 2024-01-10
|
|
10
|
++
|
|
11
|
++
### Fixed
|
|
12
|
++
* Update chia_rs to 0.2.15 for AMD K10 architecture (fixes #16386)
|
|
13
|
++
|
|
14
|
++
### Changed
|
|
15
|
++
* improved CPU usage due to tight loop in `send_transaction()`
|
|
16
|
++
* improve performance of `total_mempool_fees()` and `total_mempool_cost()`
|
|
17
|
++
* reduced the default maximum peer count to 40 from 80 (only applies to new configs)
|
|
18
|
++
* changed to `normal` SQlite db sync option (previously was `full`)
|
|
19
|
++
* reduced the mempool size to 10 blocks from 50 blocks (improves performance)
|
|
20
|
++
* improve performance of the mempool by batch fetching items from the db
|
|
21
|
++
|
|
22
|
++
|
|
9
23
|
## 2.1.3 Chia blockchain 2023-12-18
|
|
10
24
|
|
|
11
25
|
### Fixed
|
|
@@@ -61,13 -61,13 +61,13 @@@ for consensus
|
|
|
61
61
|
## Installing
|
|
62
62
|
|
|
63
63
|
Install instructions are available in the
|
|
64
|
--
[
|
|
64
|
++
[Installation Details](https://docs.chia.net/installation/)
|
|
65
65
|
section of the
|
|
66
|
--
[
|
|
66
|
++
[Chia Docs](https://docs.chia.net/introduction/).
|
|
67
67
|
|
|
68
68
|
## Running
|
|
69
69
|
|
|
70
|
--
Once installed,
|
|
71
|
--
[
|
|
72
|
--
is available
|
|
73
|
--
[
|
|
70
|
++
Once installed, an
|
|
71
|
++
[Introduction to Chia](https://docs.chia.net/introduction/)
|
|
72
|
++
guide is available in the
|
|
73
|
++
[Chia Docs](https://docs.chia.net/introduction/).
|
|
@@@ -8,18 -8,18 +8,7 @@@ from pathlib import Pat
|
|
|
8
8
|
from time import monotonic
|
|
9
9
|
from typing import List
|
|
10
10
|
|
|
11
|
--
from benchmarks.utils import
|
|
12
|
--
clvm_generator,
|
|
13
|
--
rand_bytes,
|
|
14
|
--
rand_class_group_element,
|
|
15
|
--
rand_g1,
|
|
16
|
--
rand_g2,
|
|
17
|
--
rand_hash,
|
|
18
|
--
rand_vdf,
|
|
19
|
--
rand_vdf_proof,
|
|
20
|
--
rewards,
|
|
21
|
--
setup_db,
|
|
22
|
--
)
|
|
11
|
++
from benchmarks.utils import setup_db
|
|
23
12
|
from chia.consensus.block_record import BlockRecord
|
|
24
13
|
from chia.full_node.block_store import BlockStore
|
|
25
14
|
from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
|
|
@@@ -31,6 -31,6 +20,17 @@@ from chia.types.blockchain_format.sized
|
|
|
31
20
|
from chia.types.blockchain_format.sub_epoch_summary import SubEpochSummary
|
|
32
21
|
from chia.types.full_block import FullBlock
|
|
33
22
|
from chia.util.ints import uint8, uint32, uint64, uint128
|
|
23
|
++
from tests.util.benchmarks import (
|
|
24
|
++
clvm_generator,
|
|
25
|
++
rand_bytes,
|
|
26
|
++
rand_class_group_element,
|
|
27
|
++
rand_g1,
|
|
28
|
++
rand_g2,
|
|
29
|
++
rand_hash,
|
|
30
|
++
rand_vdf,
|
|
31
|
++
rand_vdf_proof,
|
|
32
|
++
rewards,
|
|
33
|
++
)
|
|
34
34
|
|
|
35
35
|
# to run this benchmark:
|
|
36
36
|
# python -m benchmarks.coin_store
|
|
@@@ -8,11 -8,11 +8,12 @@@ from pathlib import Pat
|
|
|
8
8
|
from time import monotonic
|
|
9
9
|
from typing import List, Tuple
|
|
10
10
|
|
|
11
|
--
from benchmarks.utils import
|
|
11
|
++
from benchmarks.utils import setup_db
|
|
12
12
|
from chia.full_node.coin_store import CoinStore
|
|
13
13
|
from chia.types.blockchain_format.coin import Coin
|
|
14
14
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
15
15
|
from chia.util.ints import uint32, uint64
|
|
16
|
++
from tests.util.benchmarks import rand_hash, rewards
|
|
16
17
|
|
|
17
18
|
# to run this benchmark:
|
|
18
19
|
# python -m benchmarks.coin_store
|
|
@@@ -10,11 -10,11 +10,12 @@@ from typing import Any, Callable, Dict
|
|
|
10
10
|
|
|
11
11
|
import click
|
|
12
12
|
|
|
13
|
--
from benchmarks.utils import EnumType, get_commit_hash
|
|
13
|
++
from benchmarks.utils import EnumType, get_commit_hash
|
|
14
14
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
15
15
|
from chia.types.full_block import FullBlock
|
|
16
16
|
from chia.util.ints import uint8, uint64
|
|
17
17
|
from chia.util.streamable import Streamable, streamable
|
|
18
|
++
from tests.util.benchmarks import rand_bytes, rand_full_block, rand_hash
|
|
18
19
|
|
|
19
20
|
# to run this benchmark:
|
|
20
21
|
# python -m benchmarks.streamable
|
|
@@@ -3,36 -3,36 +3,14 @@@ from __future__ import annotation
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import enum
|
|
5
5
|
import os
|
|
6
|
--
import random
|
|
7
6
|
import subprocess
|
|
8
7
|
import sys
|
|
9
8
|
from pathlib import Path
|
|
10
|
--
from typing import Any, AsyncIterator, Generic, Optional,
|
|
9
|
++
from typing import Any, AsyncIterator, Generic, Optional, Type, TypeVar, Union
|
|
11
10
|
|
|
12
11
|
import click
|
|
13
|
--
from chia_rs import AugSchemeMPL, G1Element, G2Element
|
|
14
12
|
|
|
15
|
--
from chia.consensus.coinbase import create_farmer_coin, create_pool_coin
|
|
16
|
--
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
17
|
--
from chia.types.blockchain_format.classgroup import ClassgroupElement
|
|
18
|
--
from chia.types.blockchain_format.coin import Coin
|
|
19
|
--
from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
|
|
20
|
--
from chia.types.blockchain_format.pool_target import PoolTarget
|
|
21
|
--
from chia.types.blockchain_format.proof_of_space import ProofOfSpace
|
|
22
|
--
from chia.types.blockchain_format.reward_chain_block import RewardChainBlock
|
|
23
|
--
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
24
|
--
from chia.types.blockchain_format.sized_bytes import bytes32, bytes100
|
|
25
|
--
from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
|
|
26
|
--
from chia.types.full_block import FullBlock
|
|
27
13
|
from chia.util.db_wrapper import DBWrapper2
|
|
28
|
--
from chia.util.ints import uint8, uint32, uint64, uint128
|
|
29
|
--
|
|
30
|
--
# farmer puzzle hash
|
|
31
|
--
ph = bytes32(b"a" * 32)
|
|
32
|
--
|
|
33
|
--
with open(Path(os.path.realpath(__file__)).parent / "clvm_generator.bin", "rb") as f:
|
|
34
|
--
clvm_generator = f.read()
|
|
35
|
--
|
|
36
14
|
|
|
37
15
|
_T_Enum = TypeVar("_T_Enum", bound=enum.Enum)
|
|
38
16
|
|
|
@@@ -48,136 -48,136 +26,6 @@@ class EnumType(click.Choice, Generic[_T
|
|
|
48
26
|
return self.__enum(converted_str)
|
|
49
27
|
|
|
50
28
|
|
|
51
|
--
def rewards(height: uint32) -> Tuple[Coin, Coin]:
|
|
52
|
--
farmer_coin = create_farmer_coin(height, ph, uint64(250000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
|
|
53
|
--
pool_coin = create_pool_coin(height, ph, uint64(1750000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
|
|
54
|
--
return farmer_coin, pool_coin
|
|
55
|
--
|
|
56
|
--
|
|
57
|
--
def rand_bytes(num: int) -> bytes:
|
|
58
|
--
ret = bytearray(num)
|
|
59
|
--
for i in range(num):
|
|
60
|
--
ret[i] = random.getrandbits(8)
|
|
61
|
--
return bytes(ret)
|
|
62
|
--
|
|
63
|
--
|
|
64
|
--
def rand_hash() -> bytes32:
|
|
65
|
--
return bytes32(rand_bytes(32))
|
|
66
|
--
|
|
67
|
--
|
|
68
|
--
def rand_g1() -> G1Element:
|
|
69
|
--
sk = AugSchemeMPL.key_gen(rand_bytes(96))
|
|
70
|
--
return sk.get_g1()
|
|
71
|
--
|
|
72
|
--
|
|
73
|
--
def rand_g2() -> G2Element:
|
|
74
|
--
sk = AugSchemeMPL.key_gen(rand_bytes(96))
|
|
75
|
--
return AugSchemeMPL.sign(sk, b"foobar")
|
|
76
|
--
|
|
77
|
--
|
|
78
|
--
def rand_class_group_element() -> ClassgroupElement:
|
|
79
|
--
return ClassgroupElement(bytes100(rand_bytes(100)))
|
|
80
|
--
|
|
81
|
--
|
|
82
|
--
def rand_vdf() -> VDFInfo:
|
|
83
|
--
return VDFInfo(rand_hash(), uint64(random.randint(100000, 1000000000)), rand_class_group_element())
|
|
84
|
--
|
|
85
|
--
|
|
86
|
--
def rand_vdf_proof() -> VDFProof:
|
|
87
|
--
return VDFProof(
|
|
88
|
--
uint8(1), # witness_type
|
|
89
|
--
rand_hash(), # witness
|
|
90
|
--
bool(random.randint(0, 1)), # normalized_to_identity
|
|
91
|
--
)
|
|
92
|
--
|
|
93
|
--
|
|
94
|
--
def rand_full_block() -> FullBlock:
|
|
95
|
--
proof_of_space = ProofOfSpace(
|
|
96
|
--
rand_hash(),
|
|
97
|
--
rand_g1(),
|
|
98
|
--
None,
|
|
99
|
--
rand_g1(),
|
|
100
|
--
uint8(0),
|
|
101
|
--
rand_bytes(8 * 32),
|
|
102
|
--
)
|
|
103
|
--
|
|
104
|
--
reward_chain_block = RewardChainBlock(
|
|
105
|
--
uint128(1),
|
|
106
|
--
uint32(2),
|
|
107
|
--
uint128(3),
|
|
108
|
--
uint8(4),
|
|
109
|
--
rand_hash(),
|
|
110
|
--
proof_of_space,
|
|
111
|
--
None,
|
|
112
|
--
rand_g2(),
|
|
113
|
--
rand_vdf(),
|
|
114
|
--
None,
|
|
115
|
--
rand_g2(),
|
|
116
|
--
rand_vdf(),
|
|
117
|
--
rand_vdf(),
|
|
118
|
--
True,
|
|
119
|
--
)
|
|
120
|
--
|
|
121
|
--
pool_target = PoolTarget(
|
|
122
|
--
rand_hash(),
|
|
123
|
--
uint32(0),
|
|
124
|
--
)
|
|
125
|
--
|
|
126
|
--
foliage_block_data = FoliageBlockData(
|
|
127
|
--
rand_hash(),
|
|
128
|
--
pool_target,
|
|
129
|
--
rand_g2(),
|
|
130
|
--
rand_hash(),
|
|
131
|
--
rand_hash(),
|
|
132
|
--
)
|
|
133
|
--
|
|
134
|
--
foliage = Foliage(
|
|
135
|
--
rand_hash(),
|
|
136
|
--
rand_hash(),
|
|
137
|
--
foliage_block_data,
|
|
138
|
--
rand_g2(),
|
|
139
|
--
rand_hash(),
|
|
140
|
--
rand_g2(),
|
|
141
|
--
)
|
|
142
|
--
|
|
143
|
--
foliage_transaction_block = FoliageTransactionBlock(
|
|
144
|
--
rand_hash(),
|
|
145
|
--
uint64(0),
|
|
146
|
--
rand_hash(),
|
|
147
|
--
rand_hash(),
|
|
148
|
--
rand_hash(),
|
|
149
|
--
rand_hash(),
|
|
150
|
--
)
|
|
151
|
--
|
|
152
|
--
farmer_coin, pool_coin = rewards(uint32(0))
|
|
153
|
--
|
|
154
|
--
transactions_info = TransactionsInfo(
|
|
155
|
--
rand_hash(),
|
|
156
|
--
rand_hash(),
|
|
157
|
--
rand_g2(),
|
|
158
|
--
uint64(0),
|
|
159
|
--
uint64(1),
|
|
160
|
--
[farmer_coin, pool_coin],
|
|
161
|
--
)
|
|
162
|
--
|
|
163
|
--
full_block = FullBlock(
|
|
164
|
--
[],
|
|
165
|
--
reward_chain_block,
|
|
166
|
--
rand_vdf_proof(),
|
|
167
|
--
rand_vdf_proof(),
|
|
168
|
--
rand_vdf_proof(),
|
|
169
|
--
rand_vdf_proof(),
|
|
170
|
--
rand_vdf_proof(),
|
|
171
|
--
foliage,
|
|
172
|
--
foliage_transaction_block,
|
|
173
|
--
transactions_info,
|
|
174
|
--
SerializedProgram.from_bytes(clvm_generator),
|
|
175
|
--
[],
|
|
176
|
--
)
|
|
177
|
--
|
|
178
|
--
return full_block
|
|
179
|
--
|
|
180
|
--
|
|
181
29
|
@contextlib.asynccontextmanager
|
|
182
30
|
async def setup_db(name: Union[str, os.PathLike[str]], db_version: int) -> AsyncIterator[DBWrapper2]:
|
|
183
31
|
db_filename = Path(name)
|
|
@@@ -13,37 -13,37 +13,6 @@@ THIS_IS_MAC = platform.system().lower()
|
|
|
13
13
|
ROOT = pathlib.Path(importlib.import_module("chia").__file__).absolute().parent.parent
|
|
14
14
|
|
|
15
15
|
|
|
16
|
--
def solve_name_collision_problem(analysis):
|
|
17
|
--
"""
|
|
18
|
--
There is a collision between the `chia` file name (which is the executable)
|
|
19
|
--
and the `chia` directory, which contains non-code resources like `english.txt`.
|
|
20
|
--
We move all the resources in the zipped area so there is no
|
|
21
|
--
need to create the `chia` directory, since the names collide.
|
|
22
|
--
|
|
23
|
--
Fetching data now requires going into a zip file, so it will be slower.
|
|
24
|
--
It's best if files that are used frequently are cached.
|
|
25
|
--
|
|
26
|
--
A sample large compressible file (1 MB of `/dev/zero`), seems to be
|
|
27
|
--
about eight times slower.
|
|
28
|
--
|
|
29
|
--
Note that this hack isn't documented, but seems to work.
|
|
30
|
--
"""
|
|
31
|
--
|
|
32
|
--
zipped = []
|
|
33
|
--
datas = []
|
|
34
|
--
for data in analysis.datas:
|
|
35
|
--
if str(data[0]).startswith("chia/"):
|
|
36
|
--
zipped.append(data)
|
|
37
|
--
else:
|
|
38
|
--
datas.append(data)
|
|
39
|
--
|
|
40
|
--
# items in this field are included in the binary
|
|
41
|
--
analysis.zipped_data = zipped
|
|
42
|
--
|
|
43
|
--
# these items will be dropped in the root folder uncompressed
|
|
44
|
--
analysis.datas = datas
|
|
45
|
--
|
|
46
|
--
|
|
47
16
|
keyring_imports = collect_submodules("keyring.backends")
|
|
48
17
|
|
|
49
18
|
# keyring uses entrypoints to read keyring.backends from metadata file entry_points.txt.
|
|
@@@ -176,8 -176,8 +145,6 @@@ def add_binary(name, path_to_script, co
|
|
|
176
145
|
noarchive=False,
|
|
177
146
|
)
|
|
178
147
|
|
|
179
|
--
solve_name_collision_problem(analysis)
|
|
180
|
--
|
|
181
148
|
binary_pyz = PYZ(analysis.pure, analysis.zipped_data, cipher=block_cipher)
|
|
182
149
|
|
|
183
150
|
binary_exe = EXE(
|
|
@@@ -206,7 -206,7 +206,7 @@@ def print_min_max_derivation_for_wallet
|
|
|
206
206
|
class WalletDBReader:
|
|
207
207
|
db_wrapper: DBWrapper2 # TODO: Remove db_wrapper member
|
|
208
208
|
config = {"db_readers": 1}
|
|
209
|
--
sql_log_path = None
|
|
209
|
++
sql_log_path: Optional[Path] = None
|
|
210
210
|
verbose = False
|
|
211
211
|
|
|
212
212
|
async def get_all_wallets(self) -> List[Wallet]:
|
|
@@@ -104,10 -104,10 +104,10 @@@ def configure
|
|
|
104
104
|
if testnet == "true" or testnet == "t":
|
|
105
105
|
print("Setting Testnet")
|
|
106
106
|
testnet_port = "58444"
|
|
107
|
--
testnet_introducer = "introducer-
|
|
108
|
--
testnet_dns_introducer = "dns-introducer-
|
|
109
|
--
bootstrap_peers = ["
|
|
110
|
--
testnet = "
|
|
107
|
++
testnet_introducer = "introducer-testnet11.chia.net"
|
|
108
|
++
testnet_dns_introducer = "dns-introducer-testnet11.chia.net"
|
|
109
|
++
bootstrap_peers = ["testnet11-node-us-west-2.chia.net"]
|
|
110
|
++
testnet = "testnet11"
|
|
111
111
|
config["full_node"]["port"] = int(testnet_port)
|
|
112
112
|
if config["full_node"]["introducer_peer"] is None:
|
|
113
113
|
config["full_node"]["introducer_peer"] = {}
|
|
@@@ -121,7 -121,7 +121,7 @@@ async def print_block_from_hash
|
|
|
121
121
|
cost = str(full_block.transactions_info.cost)
|
|
122
122
|
tx_filter_hash: Union[str, bytes32] = "Not a transaction block"
|
|
123
123
|
if full_block.foliage_transaction_block:
|
|
124
|
--
tx_filter_hash = full_block.foliage_transaction_block.filter_hash
|
|
124
|
++
tx_filter_hash = bytes32(full_block.foliage_transaction_block.filter_hash)
|
|
125
125
|
fees: Any = block.fees
|
|
126
126
|
else:
|
|
127
127
|
block_time_string = "Not a transaction block"
|
|
@@@ -441,7 -441,7 +441,6 @@@ def add_token_cmd(wallet_rpc_port: Opti
|
|
|
441
441
|
"-r",
|
|
442
442
|
"--request",
|
|
443
443
|
help="A wallet id of an asset to receive and the amount you wish to receive (formatted like wallet_id:amount)",
|
|
444
|
--
required=True,
|
|
445
444
|
multiple=True,
|
|
446
445
|
)
|
|
447
446
|
@click.option("-p", "--filepath", help="The path to write the generated offer file to", required=True)
|
|
@@@ -454,6 -454,6 +453,7 @@@
|
|
|
454
453
|
is_flag=True,
|
|
455
454
|
default=False,
|
|
456
455
|
)
|
|
456
|
++
@click.option("-o", "--override", help="Creates offer without checking for unusual values", is_flag=True, default=False)
|
|
457
457
|
def make_offer_cmd(
|
|
458
458
|
wallet_rpc_port: Optional[int],
|
|
459
459
|
fingerprint: int,
|
|
@@@ -462,9 -462,9 +462,14 @@@
|
|
|
462
462
|
filepath: str,
|
|
463
463
|
fee: str,
|
|
464
464
|
reuse: bool,
|
|
465
|
++
override: bool,
|
|
465
466
|
) -> None:
|
|
466
467
|
from .wallet_funcs import make_offer
|
|
467
468
|
|
|
469
|
++
if len(request) == 0 and not override:
|
|
470
|
++
print("Cannot make an offer without requesting something without --override")
|
|
471
|
++
return
|
|
472
|
++
|
|
468
473
|
asyncio.run(
|
|
469
474
|
make_offer(
|
|
470
475
|
wallet_rpc_port=wallet_rpc_port,
|
|
@@@ -873,14 -873,14 +878,20 @@@ def did_transfer_did
|
|
|
873
878
|
id: int,
|
|
874
879
|
target_address: str,
|
|
875
880
|
reset_recovery: bool,
|
|
876
|
--
fee:
|
|
881
|
++
fee: str,
|
|
877
882
|
reuse: bool,
|
|
878
883
|
) -> None:
|
|
879
884
|
from .wallet_funcs import transfer_did
|
|
880
885
|
|
|
881
886
|
asyncio.run(
|
|
882
887
|
transfer_did(
|
|
883
|
--
wallet_rpc_port,
|
|
888
|
++
wallet_rpc_port,
|
|
889
|
++
fingerprint,
|
|
890
|
++
id,
|
|
891
|
++
Decimal(fee),
|
|
892
|
++
target_address,
|
|
893
|
++
reset_recovery is False,
|
|
894
|
++
True if reuse else None,
|
|
884
895
|
)
|
|
885
896
|
)
|
|
886
897
|
|
|
@@@ -1004,11 -1004,11 +1004,13 @@@ async def transfer_did
|
|
|
1004
1004
|
wallet_rpc_port: Optional[int],
|
|
1005
1005
|
fp: Optional[int],
|
|
1006
1006
|
did_wallet_id: int,
|
|
1007
|
--
|
|
1007
|
++
d_fee: Decimal,
|
|
1008
1008
|
target_address: str,
|
|
1009
1009
|
with_recovery: bool,
|
|
1010
1010
|
reuse_puzhash: Optional[bool],
|
|
1011
1011
|
) -> None:
|
|
1012
|
++
fee: int = int(d_fee * units["chia"])
|
|
1013
|
++
|
|
1012
1014
|
async with get_wallet_client(wallet_rpc_port, fp) as (wallet_client, fingerprint, config):
|
|
1013
1015
|
try:
|
|
1014
1016
|
response = await wallet_client.did_transfer_did(
|
|
@@@ -109,12 -109,12 +109,14 @@@ class ForkInfo
|
|
|
109
109
|
self.removals_since_fork[bytes32(spend.coin_id)] = ForkRem(bytes32(spend.puzzle_hash), height)
|
|
110
110
|
for puzzle_hash, amount, hint in spend.create_coin:
|
|
111
111
|
coin = Coin(bytes32(spend.coin_id), bytes32(puzzle_hash), uint64(amount))
|
|
112
|
--
self.additions_since_fork[coin.name()] = ForkAdd(
|
|
112
|
++
self.additions_since_fork[coin.name()] = ForkAdd(
|
|
113
|
++
coin, uint32(height), uint64(timestamp), hint, False
|
|
114
|
++
)
|
|
113
115
|
for coin in block.get_included_reward_coins():
|
|
114
116
|
assert block.foliage_transaction_block is not None
|
|
115
117
|
timestamp = block.foliage_transaction_block.timestamp
|
|
116
118
|
assert coin.name() not in self.additions_since_fork
|
|
117
|
--
self.additions_since_fork[coin.name()] = ForkAdd(coin, block.height, timestamp, None, True)
|
|
119
|
++
self.additions_since_fork[coin.name()] = ForkAdd(coin, uint32(block.height), uint64(timestamp), None, True)
|
|
118
120
|
|
|
119
121
|
|
|
120
122
|
async def validate_block_body(
|
|
@@@ -391,7 -391,7 +393,7 @@@
|
|
|
391
393
|
height,
|
|
392
394
|
height,
|
|
393
395
|
False,
|
|
394
|
--
block.foliage_transaction_block.timestamp,
|
|
396
|
++
uint64(block.foliage_transaction_block.timestamp),
|
|
395
397
|
)
|
|
396
398
|
removal_coin_records[new_unspent.name] = new_unspent
|
|
397
399
|
else:
|
|
@@@ -490,7 -490,7 +492,7 @@@
|
|
|
490
492
|
block_timestamp: uint64
|
|
491
493
|
if height < constants.SOFT_FORK2_HEIGHT:
|
|
492
494
|
# this does not happen on mainnet. testnet10 only
|
|
493
|
--
block_timestamp = block.foliage_transaction_block.timestamp # pragma: no cover
|
|
495
|
++
block_timestamp = uint64(block.foliage_transaction_block.timestamp) # pragma: no cover
|
|
494
496
|
else:
|
|
495
497
|
block_timestamp = prev_transaction_block_timestamp
|
|
496
498
|
|
|
@@@ -2,7 -2,7 +2,6 @@@ from __future__ import annotation
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import random
|
|
5
|
--
from dataclasses import replace
|
|
6
5
|
from typing import Callable, Dict, List, Optional, Sequence, Tuple
|
|
7
6
|
|
|
8
7
|
import chia_rs
|
|
@@@ -501,10 -501,10 +500,7 @@@ def unfinished_block_to_full_block
|
|
|
501
500
|
is_transaction_block,
|
|
502
501
|
)
|
|
503
502
|
if prev_block is None:
|
|
504
|
--
new_foliage = replace(
|
|
505
|
--
unfinished_block.foliage,
|
|
506
|
--
reward_block_hash=reward_chain_block.get_hash(),
|
|
507
|
--
)
|
|
503
|
++
new_foliage = unfinished_block.foliage.replace(reward_block_hash=reward_chain_block.get_hash())
|
|
508
504
|
else:
|
|
509
505
|
if is_transaction_block:
|
|
510
506
|
new_fbh = unfinished_block.foliage.foliage_transaction_block_hash
|
|
@@@ -513,8 -513,8 +509,7 @@@
|
|
|
513
509
|
new_fbh = None
|
|
514
510
|
new_fbs = None
|
|
515
511
|
assert (new_fbh is None) == (new_fbs is None)
|
|
516
|
--
new_foliage = replace(
|
|
517
|
--
unfinished_block.foliage,
|
|
512
|
++
new_foliage = unfinished_block.foliage.replace(
|
|
518
513
|
reward_block_hash=reward_chain_block.get_hash(),
|
|
519
514
|
prev_block_hash=prev_block.header_hash,
|
|
520
515
|
foliage_transaction_block_hash=new_fbh,
|
|
@@@ -1,6 -1,6 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
import dataclasses
|
|
4
3
|
import logging
|
|
5
4
|
import time
|
|
6
5
|
from typing import Optional, Tuple
|
|
@@@ -67,7 -67,7 +66,7 @@@ def validate_unfinished_header_block
|
|
|
67
66
|
if genesis_block and header_block.prev_header_hash != constants.GENESIS_CHALLENGE:
|
|
68
67
|
return None, ValidationError(Err.INVALID_PREV_BLOCK_HASH)
|
|
69
68
|
|
|
70
|
--
overflow = is_overflow_block(constants, header_block.reward_chain_block.signage_point_index)
|
|
69
|
++
overflow = is_overflow_block(constants, uint8(header_block.reward_chain_block.signage_point_index))
|
|
71
70
|
if skip_overflow_last_ss_validation and overflow:
|
|
72
71
|
if final_eos_is_already_included(header_block, blocks, expected_sub_slot_iters):
|
|
73
72
|
skip_overflow_last_ss_validation = False
|
|
@@@ -194,9 -194,9 +193,9 @@@
|
|
|
194
193
|
icc_iters_proof,
|
|
195
194
|
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.output,
|
|
196
195
|
)
|
|
197
|
--
if
|
|
198
|
--
|
|
199
|
--
number_of_iterations=icc_iters_committed
|
|
196
|
++
if (
|
|
197
|
++
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf
|
|
198
|
++
!= target_vdf_info.replace(number_of_iterations=icc_iters_committed)
|
|
200
199
|
):
|
|
201
200
|
return None, ValidationError(Err.INVALID_ICC_EOS_VDF)
|
|
202
201
|
if not skip_vdf_is_valid:
|
|
@@@ -338,9 -338,9 +337,8 @@@
|
|
|
338
337
|
else:
|
|
339
338
|
cc_eos_vdf_info_iters = expected_sub_slot_iters
|
|
340
339
|
# Check that the modified data is correct
|
|
341
|
--
if sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf !=
|
|
342
|
--
|
|
343
|
--
number_of_iterations=cc_eos_vdf_info_iters,
|
|
340
|
++
if sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf != partial_cc_vdf_info.replace(
|
|
341
|
++
number_of_iterations=cc_eos_vdf_info_iters
|
|
344
342
|
):
|
|
345
343
|
return None, ValidationError(Err.INVALID_CC_EOS_VDF, "wrong challenge chain end of slot vdf")
|
|
346
344
|
|
|
@@@ -526,13 -526,13 +524,13 @@@
|
|
|
526
524
|
sp_iters: uint64 = calculate_sp_iters(
|
|
527
525
|
constants,
|
|
528
526
|
expected_sub_slot_iters,
|
|
529
|
--
header_block.reward_chain_block.signage_point_index,
|
|
527
|
++
uint8(header_block.reward_chain_block.signage_point_index),
|
|
530
528
|
)
|
|
531
529
|
|
|
532
530
|
ip_iters: uint64 = calculate_ip_iters(
|
|
533
531
|
constants,
|
|
534
532
|
expected_sub_slot_iters,
|
|
535
|
--
header_block.reward_chain_block.signage_point_index,
|
|
533
|
++
uint8(header_block.reward_chain_block.signage_point_index),
|
|
536
534
|
required_iters,
|
|
537
535
|
)
|
|
538
536
|
if header_block.reward_chain_block.challenge_chain_sp_vdf is None:
|
|
@@@ -659,9 -659,9 +657,8 @@@
|
|
|
659
657
|
header_block.reward_chain_block.challenge_chain_sp_vdf.output,
|
|
660
658
|
)
|
|
661
659
|
|
|
662
|
--
if header_block.reward_chain_block.challenge_chain_sp_vdf !=
|
|
663
|
--
|
|
664
|
--
number_of_iterations=sp_iters,
|
|
660
|
++
if header_block.reward_chain_block.challenge_chain_sp_vdf != target_vdf_info.replace(
|
|
661
|
++
number_of_iterations=sp_iters
|
|
665
662
|
):
|
|
666
663
|
return None, ValidationError(Err.INVALID_CC_SP_VDF)
|
|
667
664
|
if not skip_vdf_is_valid:
|
|
@@@ -879,7 -879,7 +876,7 @@@ def validate_finished_header_block
|
|
|
879
876
|
ip_iters: uint64 = calculate_ip_iters(
|
|
880
877
|
constants,
|
|
881
878
|
expected_sub_slot_iters,
|
|
882
|
--
header_block.reward_chain_block.signage_point_index,
|
|
879
|
++
uint8(header_block.reward_chain_block.signage_point_index),
|
|
883
880
|
required_iters,
|
|
884
881
|
)
|
|
885
882
|
if not genesis_block:
|
|
@@@ -944,14 -944,14 +941,10 @@@
|
|
|
944
941
|
ip_vdf_iters,
|
|
945
942
|
header_block.reward_chain_block.challenge_chain_ip_vdf.output,
|
|
946
943
|
)
|
|
947
|
--
if header_block.reward_chain_block.challenge_chain_ip_vdf !=
|
|
948
|
--
|
|
949
|
--
number_of_iterations=ip_iters,
|
|
944
|
++
if header_block.reward_chain_block.challenge_chain_ip_vdf != cc_target_vdf_info.replace(
|
|
945
|
++
number_of_iterations=ip_iters
|
|
950
946
|
):
|
|
951
|
--
expected =
|
|
952
|
--
cc_target_vdf_info,
|
|
953
|
--
number_of_iterations=ip_iters,
|
|
954
|
--
)
|
|
947
|
++
expected = cc_target_vdf_info.replace(number_of_iterations=ip_iters)
|
|
955
948
|
log.error(f"{header_block.reward_chain_block.challenge_chain_ip_vdf }. expected {expected}")
|
|
956
949
|
log.error(f"Block: {header_block}")
|
|
957
950
|
return None, ValidationError(Err.INVALID_CC_IP_VDF)
|
|
@@@ -990,7 -990,7 +983,7 @@@
|
|
|
990
983
|
|
|
991
984
|
# 31. Check infused challenge chain infusion point VDF
|
|
992
985
|
if not genesis_block:
|
|
993
|
--
overflow = is_overflow_block(constants, header_block.reward_chain_block.signage_point_index)
|
|
986
|
++
overflow = is_overflow_block(constants, uint8(header_block.reward_chain_block.signage_point_index))
|
|
994
987
|
deficit = calculate_deficit(
|
|
995
988
|
constants,
|
|
996
989
|
header_block.height,
|
|
@@@ -39,7 -39,7 +39,7 @@@ def block_to_block_record
|
|
|
39
39
|
sub_slot_iters, _ = get_next_sub_slot_iters_and_difficulty(
|
|
40
40
|
constants, len(block.finished_sub_slots) > 0, prev_b, blocks
|
|
41
41
|
)
|
|
42
|
--
overflow = is_overflow_block(constants, block.reward_chain_block.signage_point_index)
|
|
42
|
++
overflow = is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index))
|
|
43
43
|
deficit = calculate_deficit(
|
|
44
44
|
constants,
|
|
45
45
|
block.height,
|
|
@@@ -62,8 -62,8 +62,8 @@@
|
|
|
62
62
|
blocks,
|
|
63
63
|
block.height,
|
|
64
64
|
blocks.block_record(prev_b.prev_hash),
|
|
65
|
--
block.finished_sub_slots[0].challenge_chain.new_difficulty,
|
|
66
|
--
block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters,
|
|
65
|
++
uint64.construct_optional(block.finished_sub_slots[0].challenge_chain.new_difficulty),
|
|
66
|
++
uint64.construct_optional(block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters),
|
|
67
67
|
)
|
|
68
68
|
if ses.get_hash() != found_ses_hash:
|
|
69
69
|
raise ValueError(Err.INVALID_SUB_EPOCH_SUMMARY)
|
|
@@@ -148,7 -148,7 +148,7 @@@ def header_block_to_sub_block_record
|
|
|
148
148
|
block.height,
|
|
149
149
|
block.weight,
|
|
150
150
|
block.total_iters,
|
|
151
|
--
block.reward_chain_block.signage_point_index,
|
|
151
|
++
uint8(block.reward_chain_block.signage_point_index),
|
|
152
152
|
block.reward_chain_block.challenge_chain_ip_vdf.output,
|
|
153
153
|
icc_output,
|
|
154
154
|
block.reward_chain_block.get_hash(),
|
|
@@@ -160,9 -160,9 +160,9 @@@
|
|
|
160
160
|
deficit,
|
|
161
161
|
overflow,
|
|
162
162
|
prev_transaction_block_height,
|
|
163
|
--
timestamp,
|
|
163
|
++
uint64.construct_optional(timestamp),
|
|
164
164
|
prev_transaction_block_hash,
|
|
165
|
--
fees,
|
|
165
|
++
uint64.construct_optional(fees),
|
|
166
166
|
reward_claims_incorporated,
|
|
167
167
|
finished_challenge_slot_hashes,
|
|
168
168
|
finished_infused_challenge_slot_hashes,
|
|
@@@ -94,7 -94,7 +94,7 @@@ def next_sub_epoch_summary
|
|
|
94
94
|
Returns:
|
|
95
95
|
object: the new sub-epoch summary
|
|
96
96
|
"""
|
|
97
|
--
signage_point_index = block.reward_chain_block.signage_point_index
|
|
97
|
++
signage_point_index = uint8(block.reward_chain_block.signage_point_index)
|
|
98
98
|
prev_b: Optional[BlockRecord] = blocks.try_block_record(block.prev_header_hash)
|
|
99
99
|
if prev_b is None or prev_b.height == 0:
|
|
100
100
|
return None
|
|
@@@ -32,7 -32,7 +32,7 @@@ from chia.util.block_cache import Block
|
|
|
32
32
|
from chia.util.condition_tools import pkm_pairs
|
|
33
33
|
from chia.util.errors import Err, ValidationError
|
|
34
34
|
from chia.util.generator_tools import get_block_header, tx_removals_and_additions
|
|
35
|
--
from chia.util.ints import uint16, uint32, uint64
|
|
35
|
++
from chia.util.ints import uint8, uint16, uint32, uint64
|
|
36
36
|
from chia.util.streamable import Streamable, streamable
|
|
37
37
|
|
|
38
38
|
log = logging.getLogger(__name__)
|
|
@@@ -257,7 -257,7 +257,7 @@@ async def pre_validate_blocks_multiproc
|
|
|
257
257
|
constants, len(block.finished_sub_slots) > 0, prev_b, block_records
|
|
258
258
|
)
|
|
259
259
|
|
|
260
|
--
overflow = is_overflow_block(constants, block.reward_chain_block.signage_point_index)
|
|
260
|
++
overflow = is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index))
|
|
261
261
|
challenge = get_block_challenge(constants, block, BlockCache(recent_blocks), prev_b is None, overflow, False)
|
|
262
262
|
if block.reward_chain_block.challenge_chain_sp_vdf is None:
|
|
263
263
|
cc_sp_hash: bytes32 = challenge
|
|
@@@ -31,7 -31,7 +31,6 @@@ from chia.plotters.plotters import get_
|
|
|
31
31
|
from chia.plotting.util import add_plot_directory
|
|
32
32
|
from chia.server.server import ssl_context_for_server
|
|
33
33
|
from chia.util.bech32m import encode_puzzle_hash
|
|
34
|
--
from chia.util.beta_metrics import BetaMetricsLogger
|
|
35
34
|
from chia.util.chia_logging import initialize_service_logging
|
|
36
35
|
from chia.util.config import load_config
|
|
37
36
|
from chia.util.errors import KeychainCurrentPassphraseIsInvalid
|
|
@@@ -1534,8 -1534,8 +1533,10 @@@ async def async_run_daemon(root_path: P
|
|
|
1534
1533
|
with Lockfile.create(daemon_launch_lock_path(root_path), timeout=1):
|
|
1535
1534
|
log.info(f"chia-blockchain version: {chia_full_version_str()}")
|
|
1536
1535
|
|
|
1537
|
--
beta_metrics
|
|
1536
|
++
beta_metrics = None
|
|
1538
1537
|
if config.get("beta", {}).get("enabled", False):
|
|
1538
|
++
from chia.util.beta_metrics import BetaMetricsLogger
|
|
1539
|
++
|
|
1539
1540
|
beta_metrics = BetaMetricsLogger(root_path)
|
|
1540
1541
|
beta_metrics.start_logging()
|
|
1541
1542
|
|
|
@@@ -43,6 -43,6 +43,10 @@@ def leaf_hash(key: bytes, value: bytes
|
|
|
43
43
|
return Program.to((key, value)).get_tree_hash() # type: ignore[no-any-return]
|
|
44
44
|
|
|
45
45
|
|
|
46
|
++
def key_hash(key: bytes) -> bytes32:
|
|
47
|
++
return Program.to(key).get_tree_hash() # type: ignore[no-any-return]
|
|
48
|
++
|
|
49
|
++
|
|
46
50
|
async def _debug_dump(db: DBWrapper2, description: str = "") -> None:
|
|
47
51
|
async with db.reader() as reader:
|
|
48
52
|
cursor = await reader.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
|
@@@ -28,6 -28,6 +28,7 @@@ from chia.data_layer.data_layer_util im
|
|
|
28
28
|
Subscription,
|
|
29
29
|
TerminalNode,
|
|
30
30
|
internal_hash,
|
|
31
|
++
key_hash,
|
|
31
32
|
leaf_hash,
|
|
32
33
|
row_to_node,
|
|
33
34
|
)
|
|
@@@ -672,35 -672,35 +673,40 @@@ class DataStore
|
|
|
672
673
|
|
|
673
674
|
return internal_nodes
|
|
674
675
|
|
|
676
|
++
async def get_keys_values_cursor(
|
|
677
|
++
self, reader: aiosqlite.Connection, root_hash: Optional[bytes32]
|
|
678
|
++
) -> aiosqlite.Cursor:
|
|
679
|
++
return await reader.execute(
|
|
680
|
++
"""
|
|
681
|
++
WITH RECURSIVE
|
|
682
|
++
tree_from_root_hash(hash, node_type, left, right, key, value, depth, rights) AS (
|
|
683
|
++
SELECT node.*, 0 AS depth, 0 AS rights FROM node WHERE node.hash == :root_hash
|
|
684
|
++
UNION ALL
|
|
685
|
++
SELECT
|
|
686
|
++
node.*,
|
|
687
|
++
tree_from_root_hash.depth + 1 AS depth,
|
|
688
|
++
CASE
|
|
689
|
++
WHEN node.hash == tree_from_root_hash.right
|
|
690
|
++
THEN tree_from_root_hash.rights + (1 << (62 - tree_from_root_hash.depth))
|
|
691
|
++
ELSE tree_from_root_hash.rights
|
|
692
|
++
END AS rights
|
|
693
|
++
FROM node, tree_from_root_hash
|
|
694
|
++
WHERE node.hash == tree_from_root_hash.left OR node.hash == tree_from_root_hash.right
|
|
695
|
++
)
|
|
696
|
++
SELECT * FROM tree_from_root_hash
|
|
697
|
++
WHERE node_type == :node_type
|
|
698
|
++
ORDER BY depth ASC, rights ASC
|
|
699
|
++
""",
|
|
700
|
++
{"root_hash": root_hash, "node_type": NodeType.TERMINAL},
|
|
701
|
++
)
|
|
702
|
++
|
|
675
703
|
async def get_keys_values(self, tree_id: bytes32, root_hash: Optional[bytes32] = None) -> List[TerminalNode]:
|
|
676
704
|
async with self.db_wrapper.reader() as reader:
|
|
677
705
|
if root_hash is None:
|
|
678
706
|
root = await self.get_tree_root(tree_id=tree_id)
|
|
679
707
|
root_hash = root.node_hash
|
|
680
|
--
cursor = await reader.execute(
|
|
681
|
--
"""
|
|
682
|
--
WITH RECURSIVE
|
|
683
|
--
tree_from_root_hash(hash, node_type, left, right, key, value, depth, rights) AS (
|
|
684
|
--
SELECT node.*, 0 AS depth, 0 AS rights FROM node WHERE node.hash == :root_hash
|
|
685
|
--
UNION ALL
|
|
686
|
--
SELECT
|
|
687
|
--
node.*,
|
|
688
|
--
tree_from_root_hash.depth + 1 AS depth,
|
|
689
|
--
CASE
|
|
690
|
--
WHEN node.hash == tree_from_root_hash.right
|
|
691
|
--
THEN tree_from_root_hash.rights + (1 << (62 - tree_from_root_hash.depth))
|
|
692
|
--
ELSE tree_from_root_hash.rights
|
|
693
|
--
END AS rights
|
|
694
|
--
FROM node, tree_from_root_hash
|
|
695
|
--
WHERE node.hash == tree_from_root_hash.left OR node.hash == tree_from_root_hash.right
|
|
696
|
--
)
|
|
697
|
--
SELECT * FROM tree_from_root_hash
|
|
698
|
--
WHERE node_type == :node_type
|
|
699
|
--
ORDER BY depth ASC, rights ASC
|
|
700
|
--
""",
|
|
701
|
--
{"root_hash": None if root_hash is None else root_hash, "node_type": NodeType.TERMINAL},
|
|
702
|
--
)
|
|
703
708
|
|
|
709
|
++
cursor = await self.get_keys_values_cursor(reader, root_hash)
|
|
704
710
|
terminal_nodes: List[TerminalNode] = []
|
|
705
711
|
async for row in cursor:
|
|
706
712
|
if row["depth"] > 62:
|
|
@@@ -722,6 -722,6 +728,26 @@@
|
|
|
722
728
|
|
|
723
729
|
return terminal_nodes
|
|
724
730
|
|
|
731
|
++
async def get_keys_values_compressed(
|
|
732
|
++
self, tree_id: bytes32, root_hash: Optional[bytes32] = None
|
|
733
|
++
) -> Dict[bytes32, bytes32]:
|
|
734
|
++
async with self.db_wrapper.reader() as reader:
|
|
735
|
++
if root_hash is None:
|
|
736
|
++
root = await self.get_tree_root(tree_id=tree_id)
|
|
737
|
++
root_hash = root.node_hash
|
|
738
|
++
|
|
739
|
++
cursor = await self.get_keys_values_cursor(reader, root_hash)
|
|
740
|
++
kv_compressed: Dict[bytes32, bytes32] = {}
|
|
741
|
++
async for row in cursor:
|
|
742
|
++
if row["depth"] > 62:
|
|
743
|
++
raise Exception("Tree depth exceeded 62, unable to guarantee left-to-right node order.")
|
|
744
|
++
node = row_to_node(row=row)
|
|
745
|
++
if not isinstance(node, TerminalNode):
|
|
746
|
++
raise Exception(f"Unexpected internal node found: {node.hash.hex()}")
|
|
747
|
++
kv_compressed[key_hash(node.key)] = leaf_hash(node.key, node.value)
|
|
748
|
++
|
|
749
|
++
return kv_compressed
|
|
750
|
++
|
|
725
751
|
async def get_node_type(self, node_hash: bytes32) -> NodeType:
|
|
726
752
|
async with self.db_wrapper.reader() as reader:
|
|
727
753
|
cursor = await reader.execute(
|
|
@@@ -795,7 -795,7 +821,7 @@@
|
|
|
795
821
|
key: bytes,
|
|
796
822
|
value: bytes,
|
|
797
823
|
tree_id: bytes32,
|
|
798
|
--
hint_keys_values: Optional[Dict[
|
|
824
|
++
hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
|
|
799
825
|
use_optimized: bool = True,
|
|
800
826
|
status: Status = Status.PENDING,
|
|
801
827
|
root: Optional[Root] = None,
|
|
@@@ -941,7 -941,7 +967,7 @@@
|
|
|
941
967
|
tree_id: bytes32,
|
|
942
968
|
reference_node_hash: Optional[bytes32],
|
|
943
969
|
side: Optional[Side],
|
|
944
|
--
hint_keys_values: Optional[Dict[
|
|
970
|
++
hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
|
|
945
971
|
use_optimized: bool = True,
|
|
946
972
|
status: Status = Status.PENDING,
|
|
947
973
|
root: Optional[Root] = None,
|
|
@@@ -959,7 -959,7 +985,7 @@@
|
|
|
959
985
|
if any(key == node.key for node in pairs):
|
|
960
986
|
raise Exception(f"Key already present: {key.hex()}")
|
|
961
987
|
else:
|
|
962
|
--
if key in hint_keys_values:
|
|
988
|
++
if key_hash(key) in hint_keys_values:
|
|
963
989
|
raise Exception(f"Key already present: {key.hex()}")
|
|
964
990
|
|
|
965
991
|
if reference_node_hash is None:
|
|
@@@ -1015,14 -1015,14 +1041,14 @@@
|
|
|
1015
1041
|
)
|
|
1016
1042
|
|
|
1017
1043
|
if hint_keys_values is not None:
|
|
1018
|
--
hint_keys_values[key] = value
|
|
1044
|
++
hint_keys_values[key_hash(key)] = leaf_hash(key, value)
|
|
1019
1045
|
return InsertResult(node_hash=new_terminal_node_hash, root=new_root)
|
|
1020
1046
|
|
|
1021
1047
|
async def delete(
|
|
1022
1048
|
self,
|
|
1023
1049
|
key: bytes,
|
|
1024
1050
|
tree_id: bytes32,
|
|
1025
|
--
hint_keys_values: Optional[Dict[
|
|
1051
|
++
hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
|
|
1026
1052
|
use_optimized: bool = True,
|
|
1027
1053
|
status: Status = Status.PENDING,
|
|
1028
1054
|
root: Optional[Root] = None,
|
|
@@@ -1031,17 -1031,17 +1057,17 @@@
|
|
|
1031
1057
|
async with self.db_wrapper.writer():
|
|
1032
1058
|
if hint_keys_values is None:
|
|
1033
1059
|
node = await self.get_node_by_key(key=key, tree_id=tree_id)
|
|
1060
|
++
node_hash = node.hash
|
|
1061
|
++
assert isinstance(node, TerminalNode)
|
|
1034
1062
|
else:
|
|
1035
|
--
if key not in hint_keys_values:
|
|
1063
|
++
if key_hash(key) not in hint_keys_values:
|
|
1036
1064
|
log.debug(f"Request to delete an unknown key ignored: {key.hex()}")
|
|
1037
1065
|
return root
|
|
1038
|
--
|
|
1039
|
--
|
|
1040
|
--
node = TerminalNode(node_hash, key, value)
|
|
1041
|
--
del hint_keys_values[key]
|
|
1066
|
++
node_hash = hint_keys_values[key_hash(key)]
|
|
1067
|
++
del hint_keys_values[key_hash(key)]
|
|
1042
1068
|
|
|
1043
1069
|
ancestors: List[InternalNode] = await self.get_ancestors_common(
|
|
1044
|
--
node_hash=
|
|
1070
|
++
node_hash=node_hash,
|
|
1045
1071
|
tree_id=tree_id,
|
|
1046
1072
|
root_hash=root_hash,
|
|
1047
1073
|
use_optimized=use_optimized,
|
|
@@@ -1056,7 -1056,7 +1082,7 @@@
|
|
|
1056
1082
|
)
|
|
1057
1083
|
|
|
1058
1084
|
parent = ancestors[0]
|
|
1059
|
--
other_hash = parent.other_child_hash(hash=
|
|
1085
|
++
other_hash = parent.other_child_hash(hash=node_hash)
|
|
1060
1086
|
|
|
1061
1087
|
if len(ancestors) == 1:
|
|
1062
1088
|
# the parent is the root so the other side will become the new root
|
|
@@@ -1106,7 -1106,7 +1132,7 @@@
|
|
|
1106
1132
|
key: bytes,
|
|
1107
1133
|
new_value: bytes,
|
|
1108
1134
|
tree_id: bytes32,
|
|
1109
|
--
hint_keys_values: Optional[Dict[
|
|
1135
|
++
hint_keys_values: Optional[Dict[bytes32, bytes32]] = None,
|
|
1110
1136
|
use_optimized: bool = True,
|
|
1111
1137
|
status: Status = Status.PENDING,
|
|
1112
1138
|
root: Optional[Root] = None,
|
|
@@@ -1134,7 -1134,7 +1160,7 @@@
|
|
|
1134
1160
|
return InsertResult(leaf_hash(key, new_value), root)
|
|
1135
1161
|
old_node_hash = old_node.hash
|
|
1136
1162
|
else:
|
|
1137
|
--
if key not in hint_keys_values:
|
|
1163
|
++
if key_hash(key) not in hint_keys_values:
|
|
1138
1164
|
log.debug(f"Key not found: {key.hex()}. Doing an autoinsert instead")
|
|
1139
1165
|
return await self.autoinsert(
|
|
1140
1166
|
key=key,
|
|
@@@ -1145,12 -1145,12 +1171,15 @@@
|
|
|
1145
1171
|
status=status,
|
|
1146
1172
|
root=root,
|
|
1147
1173
|
)
|
|
1148
|
--
|
|
1174
|
++
node_hash = hint_keys_values[key_hash(key)]
|
|
1175
|
++
node = await self.get_node(node_hash)
|
|
1176
|
++
assert isinstance(node, TerminalNode)
|
|
1177
|
++
value = node.value
|
|
1149
1178
|
if value == new_value:
|
|
1150
1179
|
log.debug(f"New value matches old value in upsert operation: {key.hex()}")
|
|
1151
1180
|
return InsertResult(leaf_hash(key, new_value), root)
|
|
1152
1181
|
old_node_hash = leaf_hash(key=key, value=value)
|
|
1153
|
--
del hint_keys_values[key]
|
|
1182
|
++
del hint_keys_values[key_hash(key)]
|
|
1154
1183
|
|
|
1155
1184
|
# create new terminal node
|
|
1156
1185
|
new_terminal_node_hash = await self._insert_terminal_node(key=key, value=new_value)
|
|
@@@ -1192,7 -1192,7 +1221,7 @@@
|
|
|
1192
1221
|
)
|
|
1193
1222
|
|
|
1194
1223
|
if hint_keys_values is not None:
|
|
1195
|
--
hint_keys_values[key] = new_value
|
|
1224
|
++
hint_keys_values[key_hash(key)] = leaf_hash(key, new_value)
|
|
1196
1225
|
return InsertResult(node_hash=new_terminal_node_hash, root=new_root)
|
|
1197
1226
|
|
|
1198
1227
|
async def clean_node_table(self, writer: aiosqlite.Connection) -> None:
|
|
@@@ -1229,7 -1229,7 +1258,7 @@@
|
|
|
1229
1258
|
if old_root.node_hash is None:
|
|
1230
1259
|
hint_keys_values = {}
|
|
1231
1260
|
else:
|
|
1232
|
--
hint_keys_values = await self.
|
|
1261
|
++
hint_keys_values = await self.get_keys_values_compressed(tree_id, root_hash=root_hash)
|
|
1233
1262
|
|
|
1234
1263
|
intermediate_root: Optional[Root] = old_root
|
|
1235
1264
|
for change in changelist:
|
|
@@@ -23,7 -23,7 +23,7 @@@ async def generate_datastore(num_nodes
|
|
|
23
23
|
os.remove(db_path)
|
|
24
24
|
|
|
25
25
|
async with DataStore.managed(database=db_path) as data_store:
|
|
26
|
--
hint_keys_values: Dict[
|
|
26
|
++
hint_keys_values: Dict[bytes32, bytes32] = {}
|
|
27
27
|
|
|
28
28
|
tree_id = bytes32(b"0" * 32)
|
|
29
29
|
await data_store.create_tree(tree_id)
|
|
@@@ -94,7 -94,7 +94,7 @@@ from chia.util.ints import uint8, uint3
|
|
|
94
94
|
from chia.util.limited_semaphore import LimitedSemaphore
|
|
95
95
|
from chia.util.log_exceptions import log_exceptions
|
|
96
96
|
from chia.util.path import path_from_root
|
|
97
|
--
from chia.util.profiler import mem_profile_task, profile_task
|
|
97
|
++
from chia.util.profiler import enable_profiler, mem_profile_task, profile_task
|
|
98
98
|
from chia.util.safe_cancel_task import cancel_task_safe
|
|
99
99
|
|
|
100
100
|
|
|
@@@ -232,7 -232,7 +232,7 @@@ class FullNode
|
|
|
232
232
|
async with DBWrapper2.managed(
|
|
233
233
|
self.db_path,
|
|
234
234
|
db_version=db_version,
|
|
235
|
--
reader_count=4,
|
|
235
|
++
reader_count=self.config.get("db_readers", 4),
|
|
236
236
|
log_path=sql_log_path,
|
|
237
237
|
synchronous=db_sync,
|
|
238
238
|
) as self._db_wrapper:
|
|
@@@ -288,6 -288,6 +288,13 @@@
|
|
|
288
288
|
if self.config.get("enable_profiler", False):
|
|
289
289
|
asyncio.create_task(profile_task(self.root_path, "node", self.log))
|
|
290
290
|
|
|
291
|
++
self.profile_block_validation = self.config.get("profile_block_validation", False)
|
|
292
|
++
if self.profile_block_validation: # pragma: no cover
|
|
293
|
++
# this is not covered by any unit tests as it's essentially test code
|
|
294
|
++
# itself. It's exercised manually when investigating performance issues
|
|
295
|
++
profile_dir = path_from_root(self.root_path, "block-validation-profile")
|
|
296
|
++
profile_dir.mkdir(parents=True, exist_ok=True)
|
|
297
|
++
|
|
291
298
|
if self.config.get("enable_memory_profiler", False):
|
|
292
299
|
asyncio.create_task(mem_profile_task(self.root_path, "node", self.log))
|
|
293
300
|
|
|
@@@ -466,7 -466,7 +473,7 @@@
|
|
|
466
473
|
peer = entry.peer
|
|
467
474
|
try:
|
|
468
475
|
inc_status, err = await self.add_transaction(entry.transaction, entry.spend_name, peer, entry.test)
|
|
469
|
--
entry.done.
|
|
476
|
++
entry.done.set((inc_status, err))
|
|
470
477
|
except asyncio.CancelledError:
|
|
471
478
|
error_stack = traceback.format_exc()
|
|
472
479
|
self.log.debug(f"Cancelling _handle_one_transaction, closing: {error_stack}")
|
|
@@@ -1147,7 -1147,7 +1154,7 @@@
|
|
|
1147
1154
|
hints_to_add, _ = get_hints_and_subscription_coin_ids(
|
|
1148
1155
|
state_change_summary,
|
|
1149
1156
|
self.subscriptions.has_coin_subscription,
|
|
1150
|
--
self.subscriptions.
|
|
1157
|
++
self.subscriptions.has_puzzle_subscription,
|
|
1151
1158
|
)
|
|
1152
1159
|
await self.hint_store.add_hints(hints_to_add)
|
|
1153
1160
|
# Note that end_height is not necessarily the peak at this
|
|
@@@ -1387,8 -1387,8 +1394,8 @@@
|
|
|
1387
1394
|
self.log.info(
|
|
1388
1395
|
f"⏲️ Finished signage point {request.index_from_challenge}/"
|
|
1389
1396
|
f"{self.constants.NUM_SPS_SUB_SLOT}: "
|
|
1390
|
--
f"CC: {request.challenge_chain_vdf.output.get_hash()} "
|
|
1391
|
--
f"RC: {request.reward_chain_vdf.output.get_hash()} "
|
|
1397
|
++
f"CC: {request.challenge_chain_vdf.output.get_hash().hex()} "
|
|
1398
|
++
f"RC: {request.reward_chain_vdf.output.get_hash().hex()} "
|
|
1392
1399
|
)
|
|
1393
1400
|
self.signage_point_times[request.index_from_challenge] = time.time()
|
|
1394
1401
|
sub_slot_tuple = self.full_node_store.get_sub_slot(request.challenge_chain_vdf.challenge)
|
|
@@@ -1456,7 -1456,7 +1463,7 @@@
|
|
|
1456
1463
|
self.log.info(
|
|
1457
1464
|
f"🌱 Updated peak to height {record.height}, weight {record.weight}, "
|
|
1458
1465
|
f"hh {record.header_hash}, "
|
|
1459
|
--
f"forked at {state_change_summary.fork_height}, rh: {record.reward_infusion_new_challenge}, "
|
|
1466
|
++
f"forked at {state_change_summary.fork_height}, rh: {record.reward_infusion_new_challenge.hex()}, "
|
|
1460
1467
|
f"total iters: {record.total_iters}, "
|
|
1461
1468
|
f"overflow: {record.overflow}, "
|
|
1462
1469
|
f"deficit: {record.deficit}, "
|
|
@@@ -1477,7 -1477,7 +1484,7 @@@
|
|
|
1477
1484
|
hints_to_add, lookup_coin_ids = get_hints_and_subscription_coin_ids(
|
|
1478
1485
|
state_change_summary,
|
|
1479
1486
|
self.subscriptions.has_coin_subscription,
|
|
1480
|
--
self.subscriptions.
|
|
1487
|
++
self.subscriptions.has_puzzle_subscription,
|
|
1481
1488
|
)
|
|
1482
1489
|
await self.hint_store.add_hints(hints_to_add)
|
|
1483
1490
|
|
|
@@@ -1591,7 -1591,7 +1598,6 @@@
|
|
|
1591
1598
|
|
|
1592
1599
|
if record.height % 1000 == 0:
|
|
1593
1600
|
# Occasionally clear data in full node store to keep memory usage small
|
|
1594
|
--
self.full_node_store.clear_seen_unfinished_blocks()
|
|
1595
1601
|
self.full_node_store.clear_old_cache_entries()
|
|
1596
1602
|
|
|
1597
1603
|
if self.sync_store.get_sync_mode() is False:
|
|
@@@ -1706,7 -1706,7 +1712,9 @@@
|
|
|
1706
1712
|
return await self.add_block(new_block, peer)
|
|
1707
1713
|
state_change_summary: Optional[StateChangeSummary] = None
|
|
1708
1714
|
ppp_result: Optional[PeakPostProcessingResult] = None
|
|
1709
|
--
async with self.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high)
|
|
1715
|
++
async with self.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.high), enable_profiler(
|
|
1716
|
++
self.profile_block_validation
|
|
1717
|
++
) as pr:
|
|
1710
1718
|
# After acquiring the lock, check again, because another asyncio thread might have added it
|
|
1711
1719
|
if self.blockchain.contains_block(header_hash):
|
|
1712
1720
|
return None
|
|
@@@ -1800,9 -1800,9 +1808,16 @@@
|
|
|
1800
1808
|
f"pre_validation time: {pre_validation_time:0.2f} seconds, "
|
|
1801
1809
|
f"post-process time: {post_process_time:0.2f} seconds, "
|
|
1802
1810
|
f"cost: {block.transactions_info.cost if block.transactions_info is not None else 'None'}"
|
|
1803
|
--
f"{percent_full_str} header_hash: {header_hash} height: {block.height}",
|
|
1811
|
++
f"{percent_full_str} header_hash: {header_hash.hex()} height: {block.height}",
|
|
1804
1812
|
)
|
|
1805
1813
|
|
|
1814
|
++
# this is not covered by any unit tests as it's essentially test code
|
|
1815
|
++
# itself. It's exercised manually when investigating performance issues
|
|
1816
|
++
if validation_time > 2 and pr is not None: # pragma: no cover
|
|
1817
|
++
pr.create_stats()
|
|
1818
|
++
profile_dir = path_from_root(self.root_path, "block-validation-profile")
|
|
1819
|
++
pr.dump_stats(profile_dir / f"{block.height}-{validation_time:0.1f}.profile")
|
|
1820
|
++
|
|
1806
1821
|
# This code path is reached if added == ADDED_AS_ORPHAN or NEW_TIP
|
|
1807
1822
|
peak = self.blockchain.get_peak()
|
|
1808
1823
|
assert peak is not None
|
|
@@@ -1816,11 -1816,11 +1831,17 @@@
|
|
|
1816
1831
|
"transaction_block": False,
|
|
1817
1832
|
"k_size": block.reward_chain_block.proof_of_space.size,
|
|
1818
1833
|
"header_hash": block.header_hash,
|
|
1834
|
++
"fork_height": None,
|
|
1835
|
++
"rolled_back_records": None,
|
|
1819
1836
|
"height": block.height,
|
|
1820
1837
|
"validation_time": validation_time,
|
|
1821
1838
|
"pre_validation_time": pre_validation_time,
|
|
1822
1839
|
}
|
|
1823
1840
|
|
|
1841
|
++
if state_change_summary is not None:
|
|
1842
|
++
state_changed_data["fork_height"] = state_change_summary.fork_height
|
|
1843
|
++
state_changed_data["rolled_back_records"] = len(state_change_summary.rolled_back_records)
|
|
1844
|
++
|
|
1824
1845
|
if block.transactions_info is not None:
|
|
1825
1846
|
state_changed_data["transaction_block"] = True
|
|
1826
1847
|
state_changed_data["block_cost"] = block.transactions_info.cost
|
|
@@@ -1872,7 -1872,7 +1893,7 @@@
|
|
|
1872
1893
|
if self.full_node_store.seen_unfinished_block(block.get_hash()):
|
|
1873
1894
|
return None
|
|
1874
1895
|
|
|
1875
|
--
block_hash = block.reward_chain_block.get_hash()
|
|
1896
|
++
block_hash = bytes32(block.reward_chain_block.get_hash())
|
|
1876
1897
|
|
|
1877
1898
|
# This searched for the trunk hash (unfinished reward hash). If we have already added a block with the same
|
|
1878
1899
|
# hash, return
|
|
@@@ -1954,10 -1954,10 +1975,6 @@@
|
|
|
1954
1975
|
validation_start = time.monotonic()
|
|
1955
1976
|
validate_result = await self.blockchain.validate_unfinished_block(block, npc_result)
|
|
1956
1977
|
if validate_result.error is not None:
|
|
1957
|
--
if validate_result.error == Err.COIN_AMOUNT_NEGATIVE.value:
|
|
1958
|
--
# TODO: remove in the future, hotfix for 1.1.5 peers to not disconnect older peers
|
|
1959
|
--
self.log.info(f"Consensus error {validate_result.error}, not disconnecting")
|
|
1960
|
--
return
|
|
1961
1978
|
raise ConsensusError(Err(validate_result.error))
|
|
1962
1979
|
validation_time = time.monotonic() - validation_start
|
|
1963
1980
|
|
|
@@@ -2097,7 -2097,7 +2114,9 @@@
|
|
|
2097
2114
|
# If not found, cache keyed on prev block
|
|
2098
2115
|
if prev_b is None:
|
|
2099
2116
|
self.full_node_store.add_to_future_ip(request)
|
|
2100
|
--
self.log.warning(
|
|
2117
|
++
self.log.warning(
|
|
2118
|
++
f"Previous block is None, infusion point {request.reward_chain_ip_vdf.challenge.hex()}"
|
|
2119
|
++
)
|
|
2101
2120
|
return None
|
|
2102
2121
|
|
|
2103
2122
|
finished_sub_slots: Optional[List[EndOfSubSlotBundle]] = self.full_node_store.get_finished_sub_slots(
|
|
@@@ -2128,7 -2128,7 +2147,7 @@@
|
|
|
2128
2147
|
+ calculate_sp_iters(
|
|
2129
2148
|
self.constants,
|
|
2130
2149
|
sub_slot_iters,
|
|
2131
|
--
unfinished_block.reward_chain_block.signage_point_index,
|
|
2150
|
++
uint8(unfinished_block.reward_chain_block.signage_point_index),
|
|
2132
2151
|
)
|
|
2133
2152
|
)
|
|
2134
2153
|
|
|
@@@ -2210,9 -2210,9 +2229,9 @@@
|
|
|
2210
2229
|
if new_infusions is not None:
|
|
2211
2230
|
self.log.info(
|
|
2212
2231
|
f"⏲️ Finished sub slot, SP {self.constants.NUM_SPS_SUB_SLOT}/{self.constants.NUM_SPS_SUB_SLOT}, "
|
|
2213
|
--
f"{end_of_slot_bundle.challenge_chain.get_hash()}, "
|
|
2232
|
++
f"{end_of_slot_bundle.challenge_chain.get_hash().hex()}, "
|
|
2214
2233
|
f"number of sub-slots: {len(self.full_node_store.finished_sub_slots)}, "
|
|
2215
|
--
f"RC hash: {end_of_slot_bundle.reward_chain.get_hash()}, "
|
|
2234
|
++
f"RC hash: {end_of_slot_bundle.reward_chain.get_hash().hex()}, "
|
|
2216
2235
|
f"Deficit {end_of_slot_bundle.reward_chain.deficit}"
|
|
2217
2236
|
)
|
|
2218
2237
|
# Reset farmer response timer for sub slot (SP 0)
|
|
@@@ -2246,7 -2246,7 +2265,7 @@@
|
|
|
2246
2265
|
else:
|
|
2247
2266
|
self.log.info(
|
|
2248
2267
|
f"End of slot not added CC challenge "
|
|
2249
|
--
f"{end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge}"
|
|
2268
|
++
f"{end_of_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge.hex()}"
|
|
2250
2269
|
)
|
|
2251
2270
|
return None, False
|
|
2252
2271
|
|
|
@@@ -2425,8 -2425,8 +2444,8 @@@
|
|
|
2425
2444
|
if field_vdf == CompressibleVDFField.CC_EOS_VDF:
|
|
2426
2445
|
for index, sub_slot in enumerate(block.finished_sub_slots):
|
|
2427
2446
|
if sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf == vdf_info:
|
|
2428
|
--
new_proofs =
|
|
2429
|
--
new_subslot =
|
|
2447
|
++
new_proofs = sub_slot.proofs.replace(challenge_chain_slot_proof=vdf_proof)
|
|
2448
|
++
new_subslot = sub_slot.replace(proofs=new_proofs)
|
|
2430
2449
|
new_finished_subslots = block.finished_sub_slots
|
|
2431
2450
|
new_finished_subslots[index] = new_subslot
|
|
2432
2451
|
new_block = dataclasses.replace(block, finished_sub_slots=new_finished_subslots)
|
|
@@@ -2437,8 -2437,8 +2456,8 @@@
|
|
|
2437
2456
|
sub_slot.infused_challenge_chain is not None
|
|
2438
2457
|
and sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf == vdf_info
|
|
2439
2458
|
):
|
|
2440
|
--
new_proofs =
|
|
2441
|
--
new_subslot =
|
|
2459
|
++
new_proofs = sub_slot.proofs.replace(infused_challenge_chain_slot_proof=vdf_proof)
|
|
2460
|
++
new_subslot = sub_slot.replace(proofs=new_proofs)
|
|
2442
2461
|
new_finished_subslots = block.finished_sub_slots
|
|
2443
2462
|
new_finished_subslots[index] = new_subslot
|
|
2444
2463
|
new_block = dataclasses.replace(block, finished_sub_slots=new_finished_subslots)
|
|
@@@ -576,7 -576,7 +576,7 @@@ class FullNodeAPI
|
|
|
576
576
|
else:
|
|
577
577
|
if self.full_node.full_node_store.get_sub_slot(request.challenge_hash) is None:
|
|
578
578
|
if request.challenge_hash != self.full_node.constants.GENESIS_CHALLENGE:
|
|
579
|
--
self.log.info(f"Don't have challenge hash {request.challenge_hash}")
|
|
579
|
++
self.log.info(f"Don't have challenge hash {request.challenge_hash.hex()}")
|
|
580
580
|
|
|
581
581
|
sp: Optional[SignagePoint] = self.full_node.full_node_store.get_signage_point_by_index(
|
|
582
582
|
request.challenge_hash,
|
|
@@@ -651,7 -651,7 +651,8 @@@
|
|
|
651
651
|
else:
|
|
652
652
|
self.log.debug(
|
|
653
653
|
f"Signage point {request.index_from_challenge} not added, CC challenge: "
|
|
654
|
--
f"{request.challenge_chain_vdf.challenge},
|
|
654
|
++
f"{request.challenge_chain_vdf.challenge.hex()}, "
|
|
655
|
++
f"RC challenge: {request.reward_chain_vdf.challenge.hex()}"
|
|
655
656
|
)
|
|
656
657
|
|
|
657
658
|
return None
|
|
@@@ -706,7 -706,7 +707,7 @@@
|
|
|
706
707
|
if sp_vdfs.rc_vdf.output.get_hash() != request.reward_chain_sp:
|
|
707
708
|
self.log.debug(
|
|
708
709
|
f"Received proof of space for a potentially old signage point {request.challenge_chain_sp}. "
|
|
709
|
--
f"Current sp: {sp_vdfs.rc_vdf.output.get_hash()}"
|
|
710
|
++
f"Current sp: {sp_vdfs.rc_vdf.output.get_hash().hex()}"
|
|
710
711
|
)
|
|
711
712
|
return None
|
|
712
713
|
|
|
@@@ -879,9 -879,9 +880,9 @@@
|
|
|
879
880
|
sub_slot_iters = peak.sub_slot_iters
|
|
880
881
|
for sub_slot in finished_sub_slots:
|
|
881
882
|
if sub_slot.challenge_chain.new_difficulty is not None:
|
|
882
|
--
difficulty = sub_slot.challenge_chain.new_difficulty
|
|
883
|
++
difficulty = uint64(sub_slot.challenge_chain.new_difficulty)
|
|
883
884
|
if sub_slot.challenge_chain.new_sub_slot_iters is not None:
|
|
884
|
--
sub_slot_iters = sub_slot.challenge_chain.new_sub_slot_iters
|
|
885
|
++
sub_slot_iters = uint64(sub_slot.challenge_chain.new_sub_slot_iters)
|
|
885
886
|
|
|
886
887
|
required_iters: uint64 = calculate_iterations_quality(
|
|
887
888
|
self.full_node.constants.DIFFICULTY_CONSTANT_FACTOR,
|
|
@@@ -1012,14 -1012,14 +1013,9 @@@
|
|
|
1012
1013
|
self.log.warning("Signature not valid. There might be a collision in plots. Ignore this during tests.")
|
|
1013
1014
|
return None
|
|
1014
1015
|
|
|
1015
|
--
fsb2 =
|
|
1016
|
--
candidate.foliage,
|
|
1017
|
--
foliage_block_data_signature=farmer_request.foliage_block_data_signature,
|
|
1018
|
--
)
|
|
1016
|
++
fsb2 = candidate.foliage.replace(foliage_block_data_signature=farmer_request.foliage_block_data_signature)
|
|
1019
1017
|
if candidate.is_transaction_block():
|
|
1020
|
--
fsb2 =
|
|
1021
|
--
fsb2, foliage_transaction_block_signature=farmer_request.foliage_transaction_block_signature
|
|
1022
|
--
)
|
|
1018
|
++
fsb2 = fsb2.replace(foliage_transaction_block_signature=farmer_request.foliage_transaction_block_signature)
|
|
1023
1019
|
|
|
1024
1020
|
new_candidate = dataclasses.replace(candidate, foliage=fsb2)
|
|
1025
1021
|
if not self.full_node.has_valid_pool_sig(new_candidate):
|
|
@@@ -1093,7 -1093,7 +1089,7 @@@
|
|
|
1093
1089
|
if not added:
|
|
1094
1090
|
self.log.error(
|
|
1095
1091
|
f"Was not able to add end of sub-slot: "
|
|
1096
|
--
f"{request.end_of_sub_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge}. "
|
|
1092
|
++
f"{request.end_of_sub_slot_bundle.challenge_chain.challenge_chain_end_of_slot_vdf.challenge.hex()}. "
|
|
1097
1093
|
f"Re-sending new-peak to timelord"
|
|
1098
1094
|
)
|
|
1099
1095
|
await self.full_node.send_peak_to_timelords(peer=peer)
|
|
@@@ -1276,7 -1276,7 +1272,7 @@@
|
|
|
1276
1272
|
await self.full_node.transaction_queue.put(queue_entry, peer_id=None, high_priority=True)
|
|
1277
1273
|
try:
|
|
1278
1274
|
with anyio.fail_after(delay=45):
|
|
1279
|
--
status, error = await queue_entry.done
|
|
1275
|
++
status, error = await queue_entry.done.wait()
|
|
1280
1276
|
except TimeoutError:
|
|
1281
1277
|
response = wallet_protocol.TransactionAck(spend_name, uint8(MempoolInclusionStatus.PENDING), None)
|
|
1282
1278
|
else:
|
|
@@@ -1496,7 -1496,7 +1492,7 @@@
|
|
|
1496
1492
|
# the returned puzzle hashes are the ones we ended up subscribing to.
|
|
1497
1493
|
# It will have filtered duplicates and ones exceeding the subscription
|
|
1498
1494
|
# limit.
|
|
1499
|
--
puzzle_hashes = self.full_node.subscriptions.
|
|
1495
|
++
puzzle_hashes = self.full_node.subscriptions.add_puzzle_subscriptions(
|
|
1500
1496
|
peer.peer_node_id, request.puzzle_hashes, max_subscriptions
|
|
1501
1497
|
)
|
|
1502
1498
|
|
|
@@@ -45,8 -45,8 +45,11 @@@ class FullNodeStore
|
|
|
45
45
|
candidate_blocks: Dict[bytes32, Tuple[uint32, UnfinishedBlock]]
|
|
46
46
|
candidate_backup_blocks: Dict[bytes32, Tuple[uint32, UnfinishedBlock]]
|
|
47
47
|
|
|
48
|
--
#
|
|
49
|
--
|
|
48
|
++
# Block hashes of unfinished blocks that we have seen recently. This is
|
|
49
|
++
# effectively a Set[bytes32] but in order to evict the oldest items first,
|
|
50
|
++
# we use a Dict that preserves insertion order, and remove from the
|
|
51
|
++
# beginning
|
|
52
|
++
seen_unfinished_blocks: Dict[bytes32, None]
|
|
50
53
|
|
|
51
54
|
# Unfinished blocks, keyed from reward hash
|
|
52
55
|
unfinished_blocks: Dict[bytes32, Tuple[uint32, UnfinishedBlock, PreValidationResult]]
|
|
@@@ -86,10 -86,10 +89,12 @@@
|
|
|
86
89
|
serialized_wp_message: Optional[Message]
|
|
87
90
|
serialized_wp_message_tip: Optional[bytes32]
|
|
88
91
|
|
|
92
|
++
max_seen_unfinished_blocks: int
|
|
93
|
++
|
|
89
94
|
def __init__(self, constants: ConsensusConstants):
|
|
90
95
|
self.candidate_blocks = {}
|
|
91
96
|
self.candidate_backup_blocks = {}
|
|
92
|
--
self.seen_unfinished_blocks =
|
|
97
|
++
self.seen_unfinished_blocks = {}
|
|
93
98
|
self.unfinished_blocks = {}
|
|
94
99
|
self.finished_sub_slots = []
|
|
95
100
|
self.future_eos_cache = {}
|
|
@@@ -108,6 -108,6 +113,7 @@@
|
|
|
108
113
|
self.tx_fetch_tasks = {}
|
|
109
114
|
self.serialized_wp_message = None
|
|
110
115
|
self.serialized_wp_message_tip = None
|
|
116
|
++
self.max_seen_unfinished_blocks = 1000
|
|
111
117
|
|
|
112
118
|
def add_candidate_block(
|
|
113
119
|
self, quality_string: bytes32, height: uint32, unfinished_block: UnfinishedBlock, backup: bool = False
|
|
@@@ -148,12 -148,12 +154,13 @@@
|
|
|
148
154
|
def seen_unfinished_block(self, object_hash: bytes32) -> bool:
|
|
149
155
|
if object_hash in self.seen_unfinished_blocks:
|
|
150
156
|
return True
|
|
151
|
--
self.seen_unfinished_blocks
|
|
157
|
++
self.seen_unfinished_blocks[object_hash] = None
|
|
158
|
++
if len(self.seen_unfinished_blocks) > self.max_seen_unfinished_blocks:
|
|
159
|
++
# remove the least recently added hash
|
|
160
|
++
to_remove = next(iter(self.seen_unfinished_blocks))
|
|
161
|
++
del self.seen_unfinished_blocks[to_remove]
|
|
152
162
|
return False
|
|
153
163
|
|
|
154
|
--
def clear_seen_unfinished_blocks(self) -> None:
|
|
155
|
--
self.seen_unfinished_blocks.clear()
|
|
156
|
--
|
|
157
164
|
def add_unfinished_block(
|
|
158
165
|
self, height: uint32, unfinished_block: UnfinishedBlock, result: PreValidationResult
|
|
159
166
|
) -> None:
|
|
@@@ -171,8 -171,8 +178,9 @@@
|
|
|
171
178
|
return None
|
|
172
179
|
return result[2]
|
|
173
180
|
|
|
174
|
--
|
|
175
|
--
|
|
181
|
++
# returns all unfinished blocks for the specified height
|
|
182
|
++
def get_unfinished_blocks(self, height: uint32) -> List[UnfinishedBlock]:
|
|
183
|
++
return [block for ub_height, block, _ in self.unfinished_blocks.values() if ub_height == height]
|
|
176
184
|
|
|
177
185
|
def clear_unfinished_blocks_below(self, height: uint32) -> None:
|
|
178
186
|
del_keys: List[bytes32] = []
|
|
@@@ -219,7 -219,7 +227,7 @@@
|
|
|
219
227
|
|
|
220
228
|
self.future_cache_key_times[signage_point.rc_vdf.challenge] = int(time.time())
|
|
221
229
|
self.future_sp_cache[signage_point.rc_vdf.challenge].append((index, signage_point))
|
|
222
|
--
log.info(f"Don't have rc hash {signage_point.rc_vdf.challenge}. caching signage point {index}.")
|
|
230
|
++
log.info(f"Don't have rc hash {signage_point.rc_vdf.challenge.hex()}. caching signage point {index}.")
|
|
223
231
|
|
|
224
232
|
def get_future_ip(self, rc_challenge_hash: bytes32) -> List[timelord_protocol.NewInfusionPointVDF]:
|
|
225
233
|
return self.future_ip_cache.get(rc_challenge_hash, [])
|
|
@@@ -287,7 -287,7 +295,7 @@@
|
|
|
287
295
|
# This prevent other peers from appending fake VDFs to our cache
|
|
288
296
|
log.error(
|
|
289
297
|
f"bad cc_challenge in new_finished_sub_slot, "
|
|
290
|
--
f"got {eos.challenge_chain.challenge_chain_end_of_slot_vdf.challenge}"
|
|
298
|
++
f"got {eos.challenge_chain.challenge_chain_end_of_slot_vdf.challenge.hex()}"
|
|
291
299
|
f"expected {cc_challenge}"
|
|
292
300
|
)
|
|
293
301
|
return None
|
|
@@@ -310,7 -310,7 +318,7 @@@
|
|
|
310
318
|
log.debug("dont add slot, total_iters < peak.total_iters")
|
|
311
319
|
return None
|
|
312
320
|
|
|
313
|
--
rc_challenge = eos.reward_chain.end_of_slot_vdf.challenge
|
|
321
|
++
rc_challenge = bytes32(eos.reward_chain.end_of_slot_vdf.challenge)
|
|
314
322
|
cc_start_element = peak.challenge_vdf_output
|
|
315
323
|
iters = uint64(total_iters - peak.total_iters)
|
|
316
324
|
if peak.reward_infusion_new_challenge != rc_challenge:
|
|
@@@ -436,9 -436,9 +444,8 @@@
|
|
|
436
444
|
eos.challenge_chain.challenge_chain_end_of_slot_vdf.output,
|
|
437
445
|
)
|
|
438
446
|
# The EOS will have the whole sub-slot iters, but the proof is only the delta, from the last peak
|
|
439
|
--
if eos.challenge_chain.challenge_chain_end_of_slot_vdf !=
|
|
440
|
--
|
|
441
|
--
number_of_iterations=sub_slot_iters,
|
|
447
|
++
if eos.challenge_chain.challenge_chain_end_of_slot_vdf != partial_cc_vdf_info.replace(
|
|
448
|
++
number_of_iterations=sub_slot_iters
|
|
442
449
|
):
|
|
443
450
|
return None
|
|
444
451
|
if not eos.proofs.challenge_chain_slot_proof.normalized_to_identity and not validate_vdf(
|
|
@@@ -487,9 -487,9 +494,8 @@@
|
|
|
487
494
|
eos.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.output,
|
|
488
495
|
)
|
|
489
496
|
# The EOS will have the whole sub-slot iters, but the proof is only the delta, from the last peak
|
|
490
|
--
if eos.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf !=
|
|
491
|
--
|
|
492
|
--
number_of_iterations=icc_iters,
|
|
497
|
++
if eos.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf != partial_icc_vdf_info.replace(
|
|
498
|
++
number_of_iterations=icc_iters
|
|
493
499
|
):
|
|
494
500
|
return None
|
|
495
501
|
if not eos.proofs.infused_challenge_chain_slot_proof.normalized_to_identity and not validate_vdf(
|
|
@@@ -615,9 -615,9 +621,7 @@@
|
|
|
615
621
|
uint64(sp_total_iters - curr.total_iters),
|
|
616
622
|
signage_point.rc_vdf.output,
|
|
617
623
|
)
|
|
618
|
--
if not signage_point.cc_vdf ==
|
|
619
|
--
cc_vdf_info_expected, number_of_iterations=delta_iters
|
|
620
|
--
):
|
|
624
|
++
if not signage_point.cc_vdf == cc_vdf_info_expected.replace(number_of_iterations=delta_iters):
|
|
621
625
|
self.add_to_future_sp(signage_point, index)
|
|
622
626
|
return False
|
|
623
627
|
if check_from_start_of_ss:
|
|
@@@ -9,7 -9,7 +9,7 @@@ from chia.types.blockchain_format.sized
|
|
|
9
9
|
def get_hints_and_subscription_coin_ids(
|
|
10
10
|
state_change_summary: StateChangeSummary,
|
|
11
11
|
has_coin_subscription: Callable[[bytes32], bool],
|
|
12
|
--
|
|
12
|
++
has_puzzle_subscription: Callable[[bytes32], bool],
|
|
13
13
|
) -> Tuple[List[Tuple[bytes32, bytes]], List[bytes32]]:
|
|
14
14
|
# Precondition: all hints passed in are max 32 bytes long
|
|
15
15
|
# Returns the hints that we need to add to the DB, and the coin ids that need to be looked up
|
|
@@@ -26,7 -26,7 +26,7 @@@
|
|
|
26
26
|
lookup_coin_ids.add(coin_id)
|
|
27
27
|
|
|
28
28
|
def add_if_ph_subscription(puzzle_hash: bytes32, coin_id: bytes32) -> None:
|
|
29
|
--
if
|
|
29
|
++
if has_puzzle_subscription(puzzle_hash):
|
|
30
30
|
lookup_coin_ids.add(coin_id)
|
|
31
31
|
|
|
32
32
|
for spend_id, puzzle_hash in state_change_summary.removals:
|
|
@@@ -9,136 -9,136 +9,196 @@@ from chia.types.blockchain_format.sized
|
|
|
9
9
|
log = logging.getLogger(__name__)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
--
|
|
13
|
--
|
|
12
|
++
@dataclass(frozen=True)
|
|
13
|
++
class SubscriptionSet:
|
|
14
|
++
_subscriptions_for_peer: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
|
|
15
|
++
_peers_for_subscription: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
|
|
16
|
++
|
|
17
|
++
def add_subscription(self, peer_id: bytes32, item: bytes32) -> bool:
|
|
18
|
++
peers = self._peers_for_subscription.setdefault(item, set())
|
|
19
|
++
|
|
20
|
++
if peer_id in peers:
|
|
21
|
++
return False
|
|
22
|
++
|
|
23
|
++
subscriptions = self._subscriptions_for_peer.setdefault(peer_id, set())
|
|
24
|
++
subscriptions.add(item)
|
|
25
|
++
peers.add(peer_id)
|
|
26
|
++
|
|
27
|
++
return True
|
|
28
|
++
|
|
29
|
++
def remove_subscription(self, peer_id: bytes32, item: bytes32) -> bool:
|
|
30
|
++
subscriptions = self._subscriptions_for_peer.get(peer_id)
|
|
31
|
++
|
|
32
|
++
if subscriptions is None or item not in subscriptions:
|
|
33
|
++
return False
|
|
34
|
++
|
|
35
|
++
peers = self._peers_for_subscription[item]
|
|
36
|
++
peers.remove(peer_id)
|
|
37
|
++
subscriptions.remove(item)
|
|
38
|
++
|
|
39
|
++
if len(subscriptions) == 0:
|
|
40
|
++
self._subscriptions_for_peer.pop(peer_id)
|
|
41
|
++
|
|
42
|
++
if len(peers) == 0:
|
|
43
|
++
self._peers_for_subscription.pop(item)
|
|
44
|
++
|
|
45
|
++
return True
|
|
46
|
++
|
|
47
|
++
def has_subscription(self, item: bytes32) -> bool:
|
|
48
|
++
return item in self._peers_for_subscription
|
|
49
|
++
|
|
50
|
++
def count_subscriptions(self, peer_id: bytes32) -> int:
|
|
51
|
++
return len(self._subscriptions_for_peer.get(peer_id, {}))
|
|
52
|
++
|
|
53
|
++
def remove_peer(self, peer_id: bytes32) -> None:
|
|
54
|
++
for item in self._subscriptions_for_peer.pop(peer_id, {}):
|
|
55
|
++
self._peers_for_subscription[item].remove(peer_id)
|
|
56
|
++
|
|
57
|
++
if len(self._peers_for_subscription[item]) == 0:
|
|
58
|
++
self._peers_for_subscription.pop(item)
|
|
59
|
++
|
|
60
|
++
def subscriptions(self, peer_id: bytes32) -> Set[bytes32]:
|
|
61
|
++
return self._subscriptions_for_peer.get(peer_id, set())
|
|
62
|
++
|
|
63
|
++
def peers(self, item: bytes32) -> Set[bytes32]:
|
|
64
|
++
return self._peers_for_subscription.get(item, set())
|
|
65
|
++
|
|
66
|
++
def total_count(self) -> int:
|
|
67
|
++
return len(self._peers_for_subscription)
|
|
68
|
++
|
|
69
|
++
|
|
14
70
|
@dataclass(frozen=True)
|
|
15
71
|
class PeerSubscriptions:
|
|
16
|
--
|
|
17
|
--
|
|
18
|
--
|
|
19
|
--
|
|
20
|
--
|
|
21
|
--
# Peer ID: Set[Coin ids]
|
|
22
|
--
_peer_coin_ids: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
|
|
23
|
--
# Peer ID: Set[puzzle_hash]
|
|
24
|
--
_peer_puzzle_hash: Dict[bytes32, Set[bytes32]] = field(default_factory=dict, init=False)
|
|
25
|
--
# Peer ID: subscription count
|
|
26
|
--
_peer_sub_counter: Dict[bytes32, int] = field(default_factory=dict, init=False)
|
|
27
|
--
|
|
28
|
--
def has_ph_subscription(self, ph: bytes32) -> bool:
|
|
29
|
--
return ph in self._ph_subscriptions
|
|
72
|
++
_puzzle_subscriptions: SubscriptionSet = field(default_factory=SubscriptionSet)
|
|
73
|
++
_coin_subscriptions: SubscriptionSet = field(default_factory=SubscriptionSet)
|
|
74
|
++
|
|
75
|
++
def has_puzzle_subscription(self, puzzle_hash: bytes32) -> bool:
|
|
76
|
++
return self._puzzle_subscriptions.has_subscription(puzzle_hash)
|
|
30
77
|
|
|
31
78
|
def has_coin_subscription(self, coin_id: bytes32) -> bool:
|
|
32
|
--
return
|
|
79
|
++
return self._coin_subscriptions.has_subscription(coin_id)
|
|
80
|
++
|
|
81
|
++
def peer_subscription_count(self, peer_id: bytes32) -> int:
|
|
82
|
++
puzzle_subscriptions = self._puzzle_subscriptions.count_subscriptions(peer_id)
|
|
83
|
++
coin_subscriptions = self._coin_subscriptions.count_subscriptions(peer_id)
|
|
84
|
++
return puzzle_subscriptions + coin_subscriptions
|
|
33
85
|
|
|
34
|
--
def
|
|
86
|
++
def add_puzzle_subscriptions(self, peer_id: bytes32, puzzle_hashes: List[bytes32], max_items: int) -> Set[bytes32]:
|
|
35
87
|
"""
|
|
36
|
--
|
|
37
|
--
fewer than requested in case:
|
|
38
|
--
* there are duplicate puzzle_hashes
|
|
39
|
--
* some puzzle hashes are already subscribed to
|
|
40
|
--
* the max_items limit is exceeded
|
|
88
|
++
Adds subscriptions until max_items is reached. Filters out duplicates and returns all additions.
|
|
41
89
|
"""
|
|
42
90
|
|
|
43
|
--
|
|
44
|
--
|
|
45
|
--
|
|
46
|
--
ret: Set[bytes32] = set()
|
|
91
|
++
subscription_count = self.peer_subscription_count(peer_id)
|
|
92
|
++
added: Set[bytes32] = set()
|
|
47
93
|
|
|
48
|
--
|
|
49
|
--
if existing_sub_count >= max_items:
|
|
94
|
++
def limit_reached() -> Set[bytes32]:
|
|
50
95
|
log.info(
|
|
51
|
--
"
|
|
52
|
--
"Not all its coin states will be reported",
|
|
96
|
++
"Peer %s attempted to exceed the subscription limit while adding puzzle subscriptions.",
|
|
53
97
|
peer_id,
|
|
54
98
|
)
|
|
55
|
--
return
|
|
99
|
++
return added
|
|
56
100
|
|
|
57
|
--
#
|
|
58
|
--
|
|
59
|
--
|
|
101
|
++
# If the subscription limit is reached, bail.
|
|
102
|
++
if subscription_count >= max_items:
|
|
103
|
++
return limit_reached()
|
|
60
104
|
|
|
61
|
--
|
|
62
|
--
|
|
63
|
--
|
|
105
|
++
# Decrement this counter to know if we've hit the subscription limit.
|
|
106
|
++
subscriptions_left = max_items - subscription_count
|
|
107
|
++
|
|
108
|
++
for puzzle_hash in puzzle_hashes:
|
|
109
|
++
if not self._puzzle_subscriptions.add_subscription(peer_id, puzzle_hash):
|
|
64
110
|
continue
|
|
65
111
|
|
|
66
|
--
ret.add(ph)
|
|
67
|
--
ph_sub.add(peer_id)
|
|
68
|
--
puzzle_hash_peers.add(ph)
|
|
69
|
--
self._peer_sub_counter[peer_id] += 1
|
|
70
112
|
subscriptions_left -= 1
|
|
113
|
++
added.add(puzzle_hash)
|
|
71
114
|
|
|
72
115
|
if subscriptions_left == 0:
|
|
73
|
--
|
|
74
|
--
|
|
75
|
--
|
|
76
|
--
|
|
77
|
--
|
|
78
|
--
|
|
79
|
--
|
|
80
|
--
|
|
81
|
--
|
|
82
|
--
|
|
83
|
--
|
|
84
|
--
|
|
85
|
--
|
|
86
|
--
if existing_sub_count >= max_items:
|
|
116
|
++
return limit_reached()
|
|
117
|
++
|
|
118
|
++
return added
|
|
119
|
++
|
|
120
|
++
def add_coin_subscriptions(self, peer_id: bytes32, coin_ids: List[bytes32], max_items: int) -> Set[bytes32]:
|
|
121
|
++
"""
|
|
122
|
++
Adds subscriptions until max_items is reached. Filters out duplicates and returns all additions.
|
|
123
|
++
"""
|
|
124
|
++
|
|
125
|
++
subscription_count = self.peer_subscription_count(peer_id)
|
|
126
|
++
added: Set[bytes32] = set()
|
|
127
|
++
|
|
128
|
++
def limit_reached() -> Set[bytes32]:
|
|
87
129
|
log.info(
|
|
88
|
--
"
|
|
130
|
++
"Peer %s attempted to exceed the subscription limit while adding coin subscriptions.",
|
|
89
131
|
peer_id,
|
|
90
132
|
)
|
|
91
|
--
return
|
|
133
|
++
return added
|
|
92
134
|
|
|
93
|
--
#
|
|
94
|
--
|
|
95
|
--
|
|
135
|
++
# If the subscription limit is reached, bail.
|
|
136
|
++
if subscription_count >= max_items:
|
|
137
|
++
return limit_reached()
|
|
138
|
++
|
|
139
|
++
# Decrement this counter to know if we've hit the subscription limit.
|
|
140
|
++
subscriptions_left = max_items - subscription_count
|
|
96
141
|
|
|
97
142
|
for coin_id in coin_ids:
|
|
98
|
--
|
|
99
|
--
if peer_id in coin_sub:
|
|
143
|
++
if not self._coin_subscriptions.add_subscription(peer_id, coin_id):
|
|
100
144
|
continue
|
|
101
145
|
|
|
102
|
--
coin_sub.add(peer_id)
|
|
103
|
--
coin_id_peers.add(coin_id)
|
|
104
|
--
self._peer_sub_counter[peer_id] += 1
|
|
105
146
|
subscriptions_left -= 1
|
|
147
|
++
added.add(coin_id)
|
|
106
148
|
|
|
107
149
|
if subscriptions_left == 0:
|
|
108
|
--
|
|
109
|
--
|
|
110
|
--
|
|
111
|
--
|
|
112
|
--
|
|
150
|
++
return limit_reached()
|
|
151
|
++
|
|
152
|
++
return added
|
|
153
|
++
|
|
154
|
++
def remove_puzzle_subscriptions(self, peer_id: bytes32, puzzle_hashes: List[bytes32]) -> Set[bytes32]:
|
|
155
|
++
"""
|
|
156
|
++
Removes subscriptions. Filters out duplicates and returns all removals.
|
|
157
|
++
"""
|
|
158
|
++
|
|
159
|
++
removed: Set[bytes32] = set()
|
|
160
|
++
|
|
161
|
++
for puzzle_hash in puzzle_hashes:
|
|
162
|
++
if not self._puzzle_subscriptions.remove_subscription(peer_id, puzzle_hash):
|
|
163
|
++
continue
|
|
164
|
++
|
|
165
|
++
removed.add(puzzle_hash)
|
|
166
|
++
|
|
167
|
++
return removed
|
|
168
|
++
|
|
169
|
++
def remove_coin_subscriptions(self, peer_id: bytes32, coin_ids: List[bytes32]) -> Set[bytes32]:
|
|
170
|
++
"""
|
|
171
|
++
Removes subscriptions. Filters out duplicates and returns all removals.
|
|
172
|
++
"""
|
|
173
|
++
|
|
174
|
++
removed: Set[bytes32] = set()
|
|
175
|
++
|
|
176
|
++
for coin_id in coin_ids:
|
|
177
|
++
if not self._coin_subscriptions.remove_subscription(peer_id, coin_id):
|
|
178
|
++
continue
|
|
179
|
++
|
|
180
|
++
removed.add(coin_id)
|
|
181
|
++
|
|
182
|
++
return removed
|
|
113
183
|
|
|
114
184
|
def remove_peer(self, peer_id: bytes32) -> None:
|
|
115
|
--
|
|
116
|
--
|
|
117
|
--
|
|
118
|
--
|
|
119
|
--
|
|
120
|
--
|
|
121
|
--
|
|
122
|
--
|
|
123
|
--
self._ph_subscriptions.pop(ph)
|
|
124
|
--
self._peer_puzzle_hash.pop(peer_id)
|
|
125
|
--
|
|
126
|
--
coin_ids = self._peer_coin_ids.get(peer_id)
|
|
127
|
--
if coin_ids is not None:
|
|
128
|
--
for coin_id in coin_ids:
|
|
129
|
--
subs = self._coin_subscriptions[coin_id]
|
|
130
|
--
subs.remove(peer_id)
|
|
131
|
--
counter += 1
|
|
132
|
--
if subs == set():
|
|
133
|
--
self._coin_subscriptions.pop(coin_id)
|
|
134
|
--
self._peer_coin_ids.pop(peer_id)
|
|
135
|
--
|
|
136
|
--
if peer_id in self._peer_sub_counter:
|
|
137
|
--
num_subs = self._peer_sub_counter.pop(peer_id)
|
|
138
|
--
assert num_subs == counter
|
|
185
|
++
self._puzzle_subscriptions.remove_peer(peer_id)
|
|
186
|
++
self._coin_subscriptions.remove_peer(peer_id)
|
|
187
|
++
|
|
188
|
++
def coin_subscriptions(self, peer_id: bytes32) -> Set[bytes32]:
|
|
189
|
++
return self._coin_subscriptions.subscriptions(peer_id)
|
|
190
|
++
|
|
191
|
++
def puzzle_subscriptions(self, peer_id: bytes32) -> Set[bytes32]:
|
|
192
|
++
return self._puzzle_subscriptions.subscriptions(peer_id)
|
|
139
193
|
|
|
140
194
|
def peers_for_coin_id(self, coin_id: bytes32) -> Set[bytes32]:
|
|
141
|
--
return self._coin_subscriptions.
|
|
195
|
++
return self._coin_subscriptions.peers(coin_id)
|
|
142
196
|
|
|
143
197
|
def peers_for_puzzle_hash(self, puzzle_hash: bytes32) -> Set[bytes32]:
|
|
144
|
--
return self.
|
|
198
|
++
return self._puzzle_subscriptions.peers(puzzle_hash)
|
|
199
|
++
|
|
200
|
++
def coin_subscription_count(self) -> int:
|
|
201
|
++
return self._coin_subscriptions.total_count()
|
|
202
|
++
|
|
203
|
++
def puzzle_subscription_count(self) -> int:
|
|
204
|
++
return self._puzzle_subscriptions.total_count()
|
|
@@@ -442,7 -442,7 +442,7 @@@ class WeightProofHandler
|
|
|
442
442
|
None,
|
|
443
443
|
None,
|
|
444
444
|
None,
|
|
445
|
--
curr.reward_chain_block.signage_point_index,
|
|
445
|
++
uint8(curr.reward_chain_block.signage_point_index),
|
|
446
446
|
None,
|
|
447
447
|
None,
|
|
448
448
|
None,
|
|
@@@ -549,7 -549,7 +549,7 @@@
|
|
|
549
549
|
curr.challenge_chain_ip_proof,
|
|
550
550
|
icc_ip_proof,
|
|
551
551
|
cc_sp_info,
|
|
552
|
--
curr.reward_chain_block.signage_point_index,
|
|
552
|
++
uint8(curr.reward_chain_block.signage_point_index),
|
|
553
553
|
None,
|
|
554
554
|
None,
|
|
555
555
|
None,
|
|
@@@ -565,7 -565,7 +565,7 @@@
|
|
|
565
565
|
if len(weight_proof.sub_epochs) == 0:
|
|
566
566
|
return False, uint32(0)
|
|
567
567
|
|
|
568
|
--
peak_height = weight_proof.recent_chain_data[-1].reward_chain_block.height
|
|
568
|
++
peak_height = uint32(weight_proof.recent_chain_data[-1].reward_chain_block.height)
|
|
569
569
|
log.info(f"validate weight proof peak height {peak_height}")
|
|
570
570
|
summaries, sub_epoch_weight_list = _validate_sub_epoch_summaries(self.constants, weight_proof)
|
|
571
571
|
if summaries is None:
|
|
@@@ -707,10 -707,10 +707,10 @@@ def _create_sub_epoch_data
|
|
|
707
707
|
) -> SubEpochData:
|
|
708
708
|
reward_chain_hash: bytes32 = sub_epoch_summary.reward_chain_hash
|
|
709
709
|
# Number of subblocks overflow in previous slot
|
|
710
|
--
previous_sub_epoch_overflows
|
|
710
|
++
previous_sub_epoch_overflows = uint8(sub_epoch_summary.num_blocks_overflow) # total in sub epoch - expected
|
|
711
711
|
# New work difficulty and iterations per sub-slot
|
|
712
|
--
sub_slot_iters: Optional[
|
|
713
|
--
new_difficulty: Optional[
|
|
712
|
++
sub_slot_iters: Optional[int] = sub_epoch_summary.new_sub_slot_iters
|
|
713
|
++
new_difficulty: Optional[int] = sub_epoch_summary.new_difficulty
|
|
714
714
|
return SubEpochData(reward_chain_hash, previous_sub_epoch_overflows, sub_slot_iters, new_difficulty)
|
|
715
715
|
|
|
716
716
|
|
|
@@@ -746,7 -746,7 +746,7 @@@ async def _challenge_block_vdfs
|
|
|
746
746
|
header_block.challenge_chain_ip_proof,
|
|
747
747
|
None,
|
|
748
748
|
cc_sp_info,
|
|
749
|
--
header_block.reward_chain_block.signage_point_index,
|
|
749
|
++
uint8(header_block.reward_chain_block.signage_point_index),
|
|
750
750
|
None,
|
|
751
751
|
None,
|
|
752
752
|
None,
|
|
@@@ -886,7 -886,7 +886,7 @@@ def _map_sub_epoch_summaries
|
|
|
886
886
|
|
|
887
887
|
# if new epoch update diff and iters
|
|
888
888
|
if data.new_difficulty is not None:
|
|
889
|
--
curr_difficulty = data.new_difficulty
|
|
889
|
++
curr_difficulty = uint64(data.new_difficulty)
|
|
890
890
|
|
|
891
891
|
# add to dict
|
|
892
892
|
summaries.append(ses)
|
|
@@@ -998,7 -998,7 +998,7 @@@ def _validate_segment
|
|
|
998
998
|
return False, uint64(0), uint64(0), uint64(0), []
|
|
999
999
|
assert sub_slot_data.signage_point_index is not None
|
|
1000
1000
|
ip_iters = ip_iters + calculate_ip_iters(
|
|
1001
|
--
constants, curr_ssi, sub_slot_data.signage_point_index, required_iters
|
|
1001
|
++
constants, curr_ssi, uint8(sub_slot_data.signage_point_index), required_iters
|
|
1002
1002
|
)
|
|
1003
1003
|
vdf_list = _get_challenge_block_vdfs(constants, idx, segment.sub_slots, curr_ssi)
|
|
1004
1004
|
to_validate.extend(vdf_list)
|
|
@@@ -1025,7 -1025,7 +1025,7 @@@ def _get_challenge_block_vdfs
|
|
|
1025
1025
|
assert sub_slot_data.signage_point_index
|
|
1026
1026
|
sp_input = ClassgroupElement.get_default_element()
|
|
1027
1027
|
if not sub_slot_data.cc_signage_point.normalized_to_identity and sub_slot_idx >= 1:
|
|
1028
|
--
is_overflow = is_overflow_block(constants, sub_slot_data.signage_point_index)
|
|
1028
|
++
is_overflow = is_overflow_block(constants, uint8(sub_slot_data.signage_point_index))
|
|
1029
1029
|
prev_ssd = sub_slots[sub_slot_idx - 1]
|
|
1030
1030
|
sp_input = sub_slot_data_vdf_input(
|
|
1031
1031
|
constants, sub_slot_data, sub_slot_idx, sub_slots, is_overflow, prev_ssd.is_end_of_slot(), ssi
|
|
@@@ -1103,7 -1103,7 +1103,7 @@@ def _validate_sub_slot_data
|
|
|
1103
1103
|
assert sub_slot_data.cc_sp_vdf_info
|
|
1104
1104
|
input = ClassgroupElement.get_default_element()
|
|
1105
1105
|
if not sub_slot_data.cc_signage_point.normalized_to_identity:
|
|
1106
|
--
is_overflow = is_overflow_block(constants, sub_slot_data.signage_point_index)
|
|
1106
|
++
is_overflow = is_overflow_block(constants, uint8(sub_slot_data.signage_point_index))
|
|
1107
1107
|
input = sub_slot_data_vdf_input(
|
|
1108
1108
|
constants, sub_slot_data, sub_slot_idx, sub_slots, is_overflow, prev_ssd.is_end_of_slot(), ssi
|
|
1109
1109
|
)
|
|
@@@ -1208,9 -1208,9 +1208,9 @@@ def validate_recent_blocks
|
|
|
1208
1208
|
last_blocks_to_validate = 100 # todo remove cap after benchmarks
|
|
1209
1209
|
for summary in summaries[:ses_idx]:
|
|
1210
1210
|
if summary.new_sub_slot_iters is not None:
|
|
1211
|
--
ssi = summary.new_sub_slot_iters
|
|
1211
|
++
ssi = uint64(summary.new_sub_slot_iters)
|
|
1212
1212
|
if summary.new_difficulty is not None:
|
|
1213
|
--
diff = summary.new_difficulty
|
|
1213
|
++
diff = uint64(summary.new_difficulty)
|
|
1214
1214
|
|
|
1215
1215
|
ses_blocks, sub_slots, transaction_blocks = 0, 0, 0
|
|
1216
1216
|
challenge, prev_challenge = recent_chain.recent_chain_data[0].reward_chain_block.pos_ss_cc_challenge_hash, None
|
|
@@@ -1226,18 -1226,18 +1226,18 @@@
|
|
|
1226
1226
|
for sub_slot in block.finished_sub_slots:
|
|
1227
1227
|
prev_challenge = sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf.challenge
|
|
1228
1228
|
challenge = sub_slot.challenge_chain.get_hash()
|
|
1229
|
--
deficit = sub_slot.reward_chain.deficit
|
|
1229
|
++
deficit = uint8(sub_slot.reward_chain.deficit)
|
|
1230
1230
|
if sub_slot.challenge_chain.subepoch_summary_hash is not None:
|
|
1231
1231
|
ses = True
|
|
1232
1232
|
assert summaries[ses_idx].get_hash() == sub_slot.challenge_chain.subepoch_summary_hash
|
|
1233
1233
|
ses_idx += 1
|
|
1234
1234
|
if sub_slot.challenge_chain.new_sub_slot_iters is not None:
|
|
1235
|
--
ssi = sub_slot.challenge_chain.new_sub_slot_iters
|
|
1235
|
++
ssi = uint64(sub_slot.challenge_chain.new_sub_slot_iters)
|
|
1236
1236
|
if sub_slot.challenge_chain.new_difficulty is not None:
|
|
1237
|
--
diff = sub_slot.challenge_chain.new_difficulty
|
|
1237
|
++
diff = uint64(sub_slot.challenge_chain.new_difficulty)
|
|
1238
1238
|
|
|
1239
1239
|
if (challenge is not None) and (prev_challenge is not None):
|
|
1240
|
--
overflow = is_overflow_block(constants, block.reward_chain_block.signage_point_index)
|
|
1240
|
++
overflow = is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index))
|
|
1241
1241
|
if not adjusted:
|
|
1242
1242
|
assert prev_block_record is not None
|
|
1243
1243
|
prev_block_record = dataclasses.replace(
|
|
@@@ -1334,7 -1334,7 +1334,7 @@@ def __validate_pospace
|
|
|
1334
1334
|
|
|
1335
1335
|
sub_slot_data: SubSlotData = segment.sub_slots[idx]
|
|
1336
1336
|
|
|
1337
|
--
if sub_slot_data.signage_point_index and is_overflow_block(constants, sub_slot_data.signage_point_index):
|
|
1337
|
++
if sub_slot_data.signage_point_index and is_overflow_block(constants, uint8(sub_slot_data.signage_point_index)):
|
|
1338
1338
|
curr_slot = segment.sub_slots[idx - 1]
|
|
1339
1339
|
assert curr_slot.cc_slot_end_info
|
|
1340
1340
|
challenge = curr_slot.cc_slot_end_info.challenge
|
|
@@@ -1391,14 -1391,14 +1391,14 @@@ def __get_rc_sub_slot
|
|
|
1391
1391
|
slots_n = 1
|
|
1392
1392
|
assert first
|
|
1393
1393
|
assert first.signage_point_index is not None
|
|
1394
|
--
if is_overflow_block(constants, first.signage_point_index):
|
|
1394
|
++
if is_overflow_block(constants, uint8(first.signage_point_index)):
|
|
1395
1395
|
if idx >= 2 and slots[idx - 2].cc_slot_end is None:
|
|
1396
1396
|
slots_n = 2
|
|
1397
1397
|
|
|
1398
1398
|
new_diff = None if ses is None else ses.new_difficulty
|
|
1399
1399
|
new_ssi = None if ses is None else ses.new_sub_slot_iters
|
|
1400
1400
|
ses_hash: Optional[bytes32] = None if ses is None else ses.get_hash()
|
|
1401
|
--
overflow = is_overflow_block(constants, first.signage_point_index)
|
|
1401
|
++
overflow = is_overflow_block(constants, uint8(first.signage_point_index))
|
|
1402
1402
|
if overflow:
|
|
1403
1403
|
if idx >= 2 and slots[idx - 2].cc_slot_end is not None and slots[idx - 1].cc_slot_end is not None:
|
|
1404
1404
|
ses_hash = None
|
|
@@@ -1483,9 -1483,9 +1483,9 @@@ def _get_curr_diff_ssi
|
|
|
1483
1483
|
curr_ssi = constants.SUB_SLOT_ITERS_STARTING
|
|
1484
1484
|
for ses in reversed(summaries[0:idx]):
|
|
1485
1485
|
if ses.new_sub_slot_iters is not None:
|
|
1486
|
--
curr_ssi = ses.new_sub_slot_iters
|
|
1486
|
++
curr_ssi = uint64(ses.new_sub_slot_iters)
|
|
1487
1487
|
assert ses.new_difficulty is not None
|
|
1488
|
--
curr_difficulty = ses.new_difficulty
|
|
1488
|
++
curr_difficulty = uint64(ses.new_difficulty)
|
|
1489
1489
|
break
|
|
1490
1490
|
|
|
1491
1491
|
return curr_difficulty, curr_ssi
|
|
@@@ -1521,7 -1521,7 +1521,7 @@@ def _get_last_ses_hash
|
|
|
1521
1521
|
if slot.challenge_chain.subepoch_summary_hash is not None:
|
|
1522
1522
|
return (
|
|
1523
1523
|
slot.challenge_chain.subepoch_summary_hash,
|
|
1524
|
--
curr.reward_chain_block.height,
|
|
1524
|
++
uint32(curr.reward_chain_block.height),
|
|
1525
1525
|
)
|
|
1526
1526
|
idx += 1
|
|
1527
1527
|
return None, uint32(0)
|
|
@@@ -1558,8 -1558,8 +1558,8 @@@ def get_sp_total_iters
|
|
|
1558
1558
|
assert sub_slot_data.cc_ip_vdf_info is not None
|
|
1559
1559
|
assert sub_slot_data.total_iters is not None
|
|
1560
1560
|
assert sub_slot_data.signage_point_index is not None
|
|
1561
|
--
sp_iters: uint64 = calculate_sp_iters(constants, ssi, sub_slot_data.signage_point_index)
|
|
1562
|
--
ip_iters: uint64 = sub_slot_data.cc_ip_vdf_info.number_of_iterations
|
|
1561
|
++
sp_iters: uint64 = calculate_sp_iters(constants, ssi, uint8(sub_slot_data.signage_point_index))
|
|
1562
|
++
ip_iters: uint64 = uint64(sub_slot_data.cc_ip_vdf_info.number_of_iterations)
|
|
1563
1563
|
sp_sub_slot_total_iters = uint128(sub_slot_data.total_iters - ip_iters)
|
|
1564
1564
|
if is_overflow:
|
|
1565
1565
|
sp_sub_slot_total_iters = uint128(sp_sub_slot_total_iters - ssi)
|
|
@@@ -1645,7 -1645,7 +1645,7 @@@ async def validate_weight_proof_inner
|
|
|
1645
1645
|
if len(weight_proof.sub_epochs) == 0:
|
|
1646
1646
|
return False, []
|
|
1647
1647
|
|
|
1648
|
--
peak_height = weight_proof.recent_chain_data[-1].reward_chain_block.height
|
|
1648
|
++
peak_height = uint32(weight_proof.recent_chain_data[-1].reward_chain_block.height)
|
|
1649
1649
|
log.info(f"validate weight proof peak height {peak_height}")
|
|
1650
1650
|
seed = summaries[-2].get_hash()
|
|
1651
1651
|
rng = random.Random(seed)
|
|
1652
|
--
|
|
1652
|
++
+ b/chia/legacy/__init__.py
|
|
@@@ -138,6 -138,6 +138,16 @@@ class Cache
|
|
|
138
138
|
cache_data: CacheDataV1 = CacheDataV1.from_bytes(stored_cache.blob)
|
|
139
139
|
self._data = {}
|
|
140
140
|
estimated_c2_sizes: Dict[int, int] = {}
|
|
141
|
++
measured_sizes: Dict[int, int] = {
|
|
142
|
++
32: 738,
|
|
143
|
++
33: 1083,
|
|
144
|
++
34: 1771,
|
|
145
|
++
35: 3147,
|
|
146
|
++
36: 5899,
|
|
147
|
++
37: 11395,
|
|
148
|
++
38: 22395,
|
|
149
|
++
39: 44367,
|
|
150
|
++
}
|
|
141
151
|
for path, cache_entry in cache_data.entries:
|
|
142
152
|
new_entry = CacheEntry(
|
|
143
153
|
DiskProver.from_bytes(cache_entry.prover_data),
|
|
@@@ -160,7 -160,7 +170,14 @@@
|
|
|
160
170
|
# static data: version(2) + table pointers (<=96) + id(32) + k(1) => ~130
|
|
161
171
|
# path: up to ~1870, all above will lead to false positive.
|
|
162
172
|
# See https://github.com/Chia-Network/chiapos/blob/3ee062b86315823dd775453ad320b8be892c7df3/src/prover_disk.hpp#L282-L287 # noqa: E501
|
|
163
|
--
|
|
173
|
++
|
|
174
|
++
# Use experimental measurements if more than estimates
|
|
175
|
++
# https://github.com/Chia-Network/chia-blockchain/issues/16063
|
|
176
|
++
check_size = estimated_c2_sizes[k] + memo_size + 2000
|
|
177
|
++
if k in measured_sizes:
|
|
178
|
++
check_size = max(check_size, measured_sizes[k])
|
|
179
|
++
|
|
180
|
++
if prover_size > check_size:
|
|
164
181
|
log.warning(
|
|
165
182
|
"Suspicious cache entry dropped. Recommended: stop the harvester, remove "
|
|
166
183
|
f"{self._path}, restart. Entry: size {prover_size}, path {path}"
|
|
@@@ -4,7 -4,7 +4,7 @@@ import loggin
|
|
|
4
4
|
from typing import List, Optional, Tuple
|
|
5
5
|
|
|
6
6
|
from chia_rs import G1Element
|
|
7
|
--
from clvm.casts import int_from_bytes
|
|
7
|
++
from clvm.casts import int_from_bytes
|
|
8
8
|
|
|
9
9
|
from chia.clvm.singleton import SINGLETON_LAUNCHER
|
|
10
10
|
from chia.consensus.block_rewards import calculate_pool_reward
|
|
@@@ -93,7 -93,7 +93,7 @@@ def create_p2_singleton_puzzle
|
|
|
93
93
|
|
|
94
94
|
def launcher_id_to_p2_puzzle_hash(launcher_id: bytes32, seconds_delay: uint64, delayed_puzzle_hash: bytes32) -> bytes32:
|
|
95
95
|
return create_p2_singleton_puzzle(
|
|
96
|
--
SINGLETON_MOD_HASH, launcher_id,
|
|
96
|
++
SINGLETON_MOD_HASH, launcher_id, seconds_delay, delayed_puzzle_hash
|
|
97
97
|
).get_tree_hash()
|
|
98
98
|
|
|
99
99
|
|
|
@@@ -102,6 -102,6 +102,7 @@@ class FullNodeRpcApi
|
|
|
102
102
|
"/get_unfinished_block_headers": self.get_unfinished_block_headers,
|
|
103
103
|
"/get_network_space": self.get_network_space,
|
|
104
104
|
"/get_additions_and_removals": self.get_additions_and_removals,
|
|
105
|
++
"/get_aggsig_additional_data": self.get_aggsig_additional_data,
|
|
105
106
|
# this function is just here for backwards-compatibility. It will probably
|
|
106
107
|
# be removed in the future
|
|
107
108
|
"/get_initial_freeze_period": self.get_initial_freeze_period,
|
|
@@@ -556,18 -556,18 +557,17 @@@
|
|
|
556
557
|
return {"headers": []}
|
|
557
558
|
|
|
558
559
|
response_headers: List[UnfinishedHeaderBlock] = []
|
|
559
|
--
for
|
|
560
|
--
|
|
561
|
--
|
|
562
|
--
|
|
563
|
--
|
|
564
|
--
|
|
565
|
--
|
|
566
|
--
|
|
567
|
--
|
|
568
|
--
|
|
569
|
--
|
|
570
|
--
response_headers.append(unfinished_header_block)
|
|
560
|
++
for block in self.service.full_node_store.get_unfinished_blocks(peak.height):
|
|
561
|
++
unfinished_header_block = UnfinishedHeaderBlock(
|
|
562
|
++
block.finished_sub_slots,
|
|
563
|
++
block.reward_chain_block,
|
|
564
|
++
block.challenge_chain_sp_proof,
|
|
565
|
++
block.reward_chain_sp_proof,
|
|
566
|
++
block.foliage,
|
|
567
|
++
block.foliage_transaction_block,
|
|
568
|
++
b"",
|
|
569
|
++
)
|
|
570
|
++
response_headers.append(unfinished_header_block)
|
|
571
571
|
return {"headers": response_headers}
|
|
572
572
|
|
|
573
573
|
async def get_network_space(self, request: Dict[str, Any]) -> EndpointResult:
|
|
@@@ -806,6 -806,6 +806,9 @@@
|
|
|
806
806
|
"removals": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in removals],
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
++
async def get_aggsig_additional_data(self, _: Dict[str, Any]) -> EndpointResult:
|
|
810
|
++
return {"additional_data": self.service.constants.AGG_SIG_ME_ADDITIONAL_DATA.hex()}
|
|
811
|
++
|
|
809
812
|
async def get_all_mempool_tx_ids(self, _: Dict[str, Any]) -> EndpointResult:
|
|
810
813
|
ids = list(self.service.mempool_manager.mempool.all_item_ids())
|
|
811
814
|
return {"tx_ids": ids}
|
|
@@@ -159,6 -159,6 +159,10 @@@ class FullNodeRpcClient(RpcClient)
|
|
|
159
159
|
response = await self.fetch("get_coin_records_by_parent_ids", d)
|
|
160
160
|
return [CoinRecord.from_json_dict(coin_record_dict_backwards_compat(coin)) for coin in response["coin_records"]]
|
|
161
161
|
|
|
162
|
++
async def get_aggsig_additional_data(self) -> bytes32:
|
|
163
|
++
result = await self.fetch("get_aggsig_additional_data", {})
|
|
164
|
++
return bytes32.from_hexstr(result["additional_data"])
|
|
165
|
++
|
|
162
166
|
async def get_coin_records_by_hint(
|
|
163
167
|
self,
|
|
164
168
|
hint: bytes32,
|
|
@@@ -3498,7 -3494,7 +3498,7 @@@ class WalletRpcApi
|
|
|
3498
3498
|
full_puzzle = nft_puzzles.create_full_puzzle(
|
|
3499
3499
|
uncurried_nft.singleton_launcher_id,
|
|
3500
3500
|
metadata,
|
|
3501
|
--
uncurried_nft.metadata_updater_hash,
|
|
3501
|
++
bytes32(uncurried_nft.metadata_updater_hash.as_atom()),
|
|
3502
3502
|
inner_puzzle,
|
|
3503
3503
|
)
|
|
3504
3504
|
|
|
@@@ -1,6 -1,6 +1,6 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
3
|
++
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
|
4
4
|
|
|
5
5
|
from chia.data_layer.data_layer_wallet import Mirror, SingletonRecord
|
|
6
6
|
from chia.pools.pool_wallet_info import PoolWalletInfo
|
|
@@@ -9,6 -9,6 +9,7 @@@ from chia.types.blockchain_format.coin
|
|
|
9
9
|
from chia.types.blockchain_format.program import Program
|
|
10
10
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
11
11
|
from chia.types.coin_record import CoinRecord
|
|
12
|
++
from chia.types.spend_bundle import SpendBundle
|
|
12
13
|
from chia.util.bech32m import encode_puzzle_hash
|
|
13
14
|
from chia.util.ints import uint16, uint32, uint64
|
|
14
15
|
from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_to_json_dicts
|
|
@@@ -41,97 -41,97 +42,117 @@@ class WalletRpcClient(RpcClient)
|
|
|
41
42
|
"""
|
|
42
43
|
|
|
43
44
|
# Key Management APIs
|
|
44
|
--
async def log_in(self, fingerprint: int) -> Dict:
|
|
45
|
++
async def log_in(self, fingerprint: int) -> Union[Dict[str, Any], Any]:
|
|
45
46
|
try:
|
|
46
|
--
return await self.fetch(
|
|
47
|
--
"log_in",
|
|
48
|
--
{"fingerprint": fingerprint, "type": "start"},
|
|
49
|
--
)
|
|
50
|
--
|
|
47
|
++
return await self.fetch("log_in", {"fingerprint": fingerprint, "type": "start"})
|
|
51
48
|
except ValueError as e:
|
|
52
49
|
return e.args[0]
|
|
53
50
|
|
|
54
51
|
async def set_wallet_resync_on_startup(self, enable: bool = True) -> Dict[str, Any]:
|
|
55
|
--
return await self.fetch(
|
|
56
|
--
path="set_wallet_resync_on_startup",
|
|
57
|
--
request_json={"enable": enable},
|
|
58
|
--
)
|
|
52
|
++
return await self.fetch(path="set_wallet_resync_on_startup", request_json={"enable": enable})
|
|
59
53
|
|
|
60
|
--
async def get_logged_in_fingerprint(self) -> int:
|
|
61
|
--
|
|
54
|
++
async def get_logged_in_fingerprint(self) -> Optional[int]:
|
|
55
|
++
response = await self.fetch("get_logged_in_fingerprint", {})
|
|
56
|
++
# TODO: casting due to lack of type checked deserialization
|
|
57
|
++
return cast(Optional[int], response["fingerprint"])
|
|
62
58
|
|
|
63
59
|
async def get_public_keys(self) -> List[int]:
|
|
64
|
--
|
|
60
|
++
response = await self.fetch("get_public_keys", {})
|
|
61
|
++
# TODO: casting due to lack of type checked deserialization
|
|
62
|
++
return cast(List[int], response["public_key_fingerprints"])
|
|
65
63
|
|
|
66
|
--
async def get_private_key(self, fingerprint: int) -> Dict:
|
|
67
|
--
|
|
64
|
++
async def get_private_key(self, fingerprint: int) -> Dict[str, Any]:
|
|
65
|
++
request = {"fingerprint": fingerprint}
|
|
66
|
++
response = await self.fetch("get_private_key", request)
|
|
67
|
++
# TODO: casting due to lack of type checked deserialization
|
|
68
|
++
return cast(Dict[str, Any], response["private_key"])
|
|
68
69
|
|
|
69
70
|
async def generate_mnemonic(self) -> List[str]:
|
|
70
|
--
|
|
71
|
++
response = await self.fetch("generate_mnemonic", {})
|
|
72
|
++
# TODO: casting due to lack of type checked deserialization
|
|
73
|
++
return cast(List[str], response["mnemonic"])
|
|
71
74
|
|
|
72
75
|
async def add_key(self, mnemonic: List[str], request_type: str = "new_wallet") -> Dict[str, Any]:
|
|
73
|
--
|
|
76
|
++
request = {"mnemonic": mnemonic, "type": request_type}
|
|
77
|
++
return await self.fetch("add_key", request)
|
|
74
78
|
|
|
75
79
|
async def delete_key(self, fingerprint: int) -> Dict[str, Any]:
|
|
76
|
--
|
|
80
|
++
request = {"fingerprint": fingerprint}
|
|
81
|
++
return await self.fetch("delete_key", request)
|
|
77
82
|
|
|
78
83
|
async def check_delete_key(self, fingerprint: int, max_ph_to_search: int = 100) -> Dict[str, Any]:
|
|
79
|
--
|
|
84
|
++
request = {"fingerprint": fingerprint, "max_ph_to_search": max_ph_to_search}
|
|
85
|
++
return await self.fetch("check_delete_key", request)
|
|
80
86
|
|
|
81
87
|
async def delete_all_keys(self) -> Dict[str, Any]:
|
|
82
88
|
return await self.fetch("delete_all_keys", {})
|
|
83
89
|
|
|
84
90
|
# Wallet Node APIs
|
|
85
91
|
async def get_sync_status(self) -> bool:
|
|
86
|
--
|
|
92
|
++
response = await self.fetch("get_sync_status", {})
|
|
93
|
++
# TODO: casting due to lack of type checked deserialization
|
|
94
|
++
return cast(bool, response["syncing"])
|
|
87
95
|
|
|
88
96
|
async def get_synced(self) -> bool:
|
|
89
|
--
|
|
97
|
++
response = await self.fetch("get_sync_status", {})
|
|
98
|
++
# TODO: casting due to lack of type checked deserialization
|
|
99
|
++
return cast(bool, response["synced"])
|
|
90
100
|
|
|
91
101
|
async def get_height_info(self) -> uint32:
|
|
92
|
--
|
|
102
|
++
response = await self.fetch("get_height_info", {})
|
|
103
|
++
# TODO: casting due to lack of type checked deserialization
|
|
104
|
++
return cast(uint32, response["height"])
|
|
93
105
|
|
|
94
|
--
async def push_tx(self, spend_bundle):
|
|
106
|
++
async def push_tx(self, spend_bundle: SpendBundle) -> Dict[str, Any]:
|
|
95
107
|
return await self.fetch("push_tx", {"spend_bundle": bytes(spend_bundle).hex()})
|
|
96
108
|
|
|
97
|
--
async def push_transactions(self, txs: List[TransactionRecord]):
|
|
109
|
++
async def push_transactions(self, txs: List[TransactionRecord]) -> Dict[str, Any]:
|
|
98
110
|
transactions = [bytes(tx).hex() for tx in txs]
|
|
99
|
--
|
|
100
111
|
return await self.fetch("push_transactions", {"transactions": transactions})
|
|
101
112
|
|
|
102
113
|
async def farm_block(self, address: str) -> Dict[str, Any]:
|
|
103
114
|
return await self.fetch("farm_block", {"address": address})
|
|
104
115
|
|
|
105
116
|
async def get_timestamp_for_height(self, height: uint32) -> uint64:
|
|
106
|
--
|
|
117
|
++
request = {"height": height}
|
|
118
|
++
response = await self.fetch("get_timestamp_for_height", request)
|
|
119
|
++
# TODO: casting due to lack of type checked deserialization
|
|
120
|
++
return cast(uint64, response["timestamp"])
|
|
107
121
|
|
|
108
122
|
# Wallet Management APIs
|
|
109
123
|
async def get_wallets(self, wallet_type: Optional[WalletType] = None) -> List[Dict[str, Any]]:
|
|
110
|
--
|
|
111
|
--
|
|
112
|
--
|
|
113
|
--
|
|
124
|
++
if wallet_type is None:
|
|
125
|
++
request = {}
|
|
126
|
++
else:
|
|
127
|
++
request = {"type": wallet_type}
|
|
128
|
++
response = await self.fetch("get_wallets", request)
|
|
129
|
++
# TODO: casting due to lack of type checked deserialization
|
|
130
|
++
return cast(List[Dict[str, Any]], response["wallets"])
|
|
114
131
|
|
|
115
132
|
# Wallet APIs
|
|
116
|
--
async def get_wallet_balance(self, wallet_id: int) -> Dict:
|
|
117
|
--
|
|
133
|
++
async def get_wallet_balance(self, wallet_id: int) -> Dict[str, Any]:
|
|
134
|
++
request = {"wallet_id": wallet_id}
|
|
135
|
++
response = await self.fetch("get_wallet_balance", request)
|
|
136
|
++
# TODO: casting due to lack of type checked deserialization
|
|
137
|
++
return cast(Dict[str, Any], response["wallet_balance"])
|
|
118
138
|
|
|
119
|
--
async def get_wallet_balances(self, wallet_ids: Optional[List[int]] = None) -> Dict:
|
|
120
|
--
|
|
139
|
++
async def get_wallet_balances(self, wallet_ids: Optional[List[int]] = None) -> Dict[str, Dict[str, Any]]:
|
|
140
|
++
request = {"wallet_ids": wallet_ids}
|
|
141
|
++
response = await self.fetch("get_wallet_balances", request)
|
|
142
|
++
# TODO: casting due to lack of type checked deserialization
|
|
143
|
++
return cast(Dict[str, Dict[str, Any]], response["wallet_balances"])
|
|
121
144
|
|
|
122
145
|
async def get_transaction(self, wallet_id: int, transaction_id: bytes32) -> TransactionRecord:
|
|
123
|
--
|
|
124
|
--
|
|
125
|
--
|
|
126
|
--
)
|
|
127
|
--
return TransactionRecord.from_json_dict_convenience(res["transaction"])
|
|
146
|
++
request = {"walled_id": wallet_id, "transaction_id": transaction_id.hex()}
|
|
147
|
++
response = await self.fetch("get_transaction", request)
|
|
148
|
++
return TransactionRecord.from_json_dict_convenience(response["transaction"])
|
|
128
149
|
|
|
129
150
|
async def get_transactions(
|
|
130
151
|
self,
|
|
131
152
|
wallet_id: int,
|
|
132
|
--
start: int = None,
|
|
133
|
--
end: int = None,
|
|
134
|
--
sort_key: SortKey = None,
|
|
153
|
++
start: Optional[int] = None,
|
|
154
|
++
end: Optional[int] = None,
|
|
155
|
++
sort_key: Optional[SortKey] = None,
|
|
135
156
|
reverse: bool = False,
|
|
136
157
|
to_address: Optional[str] = None,
|
|
137
158
|
type_filter: Optional[TransactionTypeFilter] = None,
|
|
@@@ -156,29 -156,29 +177,26 @@@
|
|
|
156
177
|
if confirmed is not None:
|
|
157
178
|
request["confirmed"] = confirmed
|
|
158
179
|
|
|
159
|
--
res = await self.fetch(
|
|
160
|
--
"get_transactions",
|
|
161
|
--
request,
|
|
162
|
--
)
|
|
180
|
++
res = await self.fetch("get_transactions", request)
|
|
163
181
|
return [TransactionRecord.from_json_dict_convenience(tx) for tx in res["transactions"]]
|
|
164
182
|
|
|
165
183
|
async def get_transaction_count(
|
|
166
|
--
self,
|
|
167
|
--
|
|
168
|
--
confirmed: Optional[bool] = None,
|
|
169
|
--
type_filter: Optional[TransactionTypeFilter] = None,
|
|
170
|
--
) -> List[TransactionRecord]:
|
|
184
|
++
self, wallet_id: int, confirmed: Optional[bool] = None, type_filter: Optional[TransactionTypeFilter] = None
|
|
185
|
++
) -> int:
|
|
171
186
|
request: Dict[str, Any] = {"wallet_id": wallet_id}
|
|
172
187
|
if type_filter is not None:
|
|
173
188
|
request["type_filter"] = type_filter.to_json_dict()
|
|
174
|
--
|
|
175
189
|
if confirmed is not None:
|
|
176
190
|
request["confirmed"] = confirmed
|
|
177
191
|
res = await self.fetch("get_transaction_count", request)
|
|
178
|
--
|
|
192
|
++
# TODO: casting due to lack of type checked deserialization
|
|
193
|
++
return cast(int, res["count"])
|
|
179
194
|
|
|
180
195
|
async def get_next_address(self, wallet_id: int, new_address: bool) -> str:
|
|
181
|
--
|
|
196
|
++
request = {"wallet_id": wallet_id, "new_address": new_address}
|
|
197
|
++
response = await self.fetch("get_next_address", request)
|
|
198
|
++
# TODO: casting due to lack of type checked deserialization
|
|
199
|
++
return cast(str, response["address"])
|
|
182
200
|
|
|
183
201
|
async def send_transaction(
|
|
184
202
|
self,
|
|
@@@ -212,9 -212,9 +230,9 @@@
|
|
|
212
230
|
async def send_transaction_multi(
|
|
213
231
|
self,
|
|
214
232
|
wallet_id: int,
|
|
215
|
--
additions: List[Dict],
|
|
233
|
++
additions: List[Dict[str, Any]],
|
|
216
234
|
tx_config: TXConfig,
|
|
217
|
--
coins: List[Coin] = None,
|
|
235
|
++
coins: Optional[List[Coin]] = None,
|
|
218
236
|
fee: uint64 = uint64(0),
|
|
219
237
|
push: bool = True,
|
|
220
238
|
) -> TransactionRecord:
|
|
@@@ -244,40 -244,40 +262,38 @@@
|
|
|
244
262
|
force: bool = False,
|
|
245
263
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
246
264
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
247
|
--
) -> Dict:
|
|
248
|
--
|
|
249
|
--
"
|
|
250
|
--
|
|
251
|
--
|
|
252
|
--
|
|
253
|
--
|
|
254
|
--
|
|
255
|
--
|
|
256
|
--
},
|
|
257
|
--
)
|
|
265
|
++
) -> Dict[str, Any]:
|
|
266
|
++
request = {
|
|
267
|
++
"coin_ids": [cid.hex() for cid in coin_ids],
|
|
268
|
++
"fee": fee,
|
|
269
|
++
"force": force,
|
|
270
|
++
"extra_conditions": conditions_to_json_dicts(extra_conditions),
|
|
271
|
++
**timelock_info.to_json_dict(),
|
|
272
|
++
}
|
|
273
|
++
response = await self.fetch("spend_clawback_coins", request)
|
|
258
274
|
return response
|
|
259
275
|
|
|
260
276
|
async def delete_unconfirmed_transactions(self, wallet_id: int) -> None:
|
|
261
|
--
await self.fetch(
|
|
262
|
--
"delete_unconfirmed_transactions",
|
|
263
|
--
{"wallet_id": wallet_id},
|
|
264
|
--
)
|
|
265
|
--
return None
|
|
277
|
++
await self.fetch("delete_unconfirmed_transactions", {"wallet_id": wallet_id})
|
|
266
278
|
|
|
267
279
|
async def get_current_derivation_index(self) -> str:
|
|
268
|
--
|
|
280
|
++
response = await self.fetch("get_current_derivation_index", {})
|
|
281
|
++
index = response["index"]
|
|
282
|
++
return str(index)
|
|
269
283
|
|
|
270
284
|
async def extend_derivation_index(self, index: int) -> str:
|
|
271
|
--
|
|
285
|
++
response = await self.fetch("extend_derivation_index", {"index": index})
|
|
286
|
++
updated_index = response["index"]
|
|
287
|
++
return str(updated_index)
|
|
272
288
|
|
|
273
|
--
async def get_farmed_amount(self) -> Dict:
|
|
289
|
++
async def get_farmed_amount(self) -> Dict[str, Any]:
|
|
274
290
|
return await self.fetch("get_farmed_amount", {})
|
|
275
291
|
|
|
276
292
|
async def create_signed_transactions(
|
|
277
293
|
self,
|
|
278
|
--
additions: List[Dict],
|
|
294
|
++
additions: List[Dict[str, Any]],
|
|
279
295
|
tx_config: TXConfig,
|
|
280
|
--
coins: List[Coin] = None,
|
|
296
|
++
coins: Optional[List[Coin]] = None,
|
|
281
297
|
fee: uint64 = uint64(0),
|
|
282
298
|
wallet_id: Optional[int] = None,
|
|
283
299
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
@@@ -291,7 -291,7 +307,7 @@@
|
|
|
291
307
|
if "memos" in ad:
|
|
292
308
|
additions_hex[-1]["memos"] = ad["memos"]
|
|
293
309
|
|
|
294
|
--
request
|
|
310
|
++
request = {
|
|
295
311
|
"additions": additions_hex,
|
|
296
312
|
"fee": fee,
|
|
297
313
|
"extra_conditions": conditions_to_json_dicts(extra_conditions),
|
|
@@@ -307,14 -307,14 +323,14 @@@
|
|
|
307
323
|
if wallet_id:
|
|
308
324
|
request["wallet_id"] = wallet_id
|
|
309
325
|
|
|
310
|
--
response
|
|
326
|
++
response = await self.fetch("create_signed_transaction", request)
|
|
311
327
|
return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["signed_txs"]]
|
|
312
328
|
|
|
313
329
|
async def create_signed_transaction(
|
|
314
330
|
self,
|
|
315
|
--
additions: List[Dict],
|
|
331
|
++
additions: List[Dict[str, Any]],
|
|
316
332
|
tx_config: TXConfig,
|
|
317
|
--
coins: List[Coin] = None,
|
|
333
|
++
coins: Optional[List[Coin]] = None,
|
|
318
334
|
fee: uint64 = uint64(0),
|
|
319
335
|
wallet_id: Optional[int] = None,
|
|
320
336
|
push: bool = False,
|
|
@@@ -336,36 -336,36 +352,22 @@@
|
|
|
336
352
|
|
|
337
353
|
return txs[0]
|
|
338
354
|
|
|
339
|
--
async def select_coins(
|
|
340
|
--
|
|
341
|
--
|
|
342
|
--
wallet_id: int,
|
|
343
|
--
coin_selection_config: CoinSelectionConfig,
|
|
344
|
--
) -> List[Coin]:
|
|
345
|
--
request = {
|
|
346
|
--
"amount": amount,
|
|
347
|
--
"wallet_id": wallet_id,
|
|
348
|
--
**coin_selection_config.to_json_dict(),
|
|
349
|
--
}
|
|
350
|
--
response: Dict[str, List[Dict]] = await self.fetch("select_coins", request)
|
|
355
|
++
async def select_coins(self, amount: int, wallet_id: int, coin_selection_config: CoinSelectionConfig) -> List[Coin]:
|
|
356
|
++
request = {"amount": amount, "wallet_id": wallet_id, **coin_selection_config.to_json_dict()}
|
|
357
|
++
response = await self.fetch("select_coins", request)
|
|
351
358
|
return [Coin.from_json_dict(coin) for coin in response["coins"]]
|
|
352
359
|
|
|
353
360
|
async def get_coin_records(self, request: GetCoinRecords) -> Dict[str, Any]:
|
|
354
361
|
return await self.fetch("get_coin_records", request.to_json_dict())
|
|
355
362
|
|
|
356
363
|
async def get_spendable_coins(
|
|
357
|
--
self,
|
|
358
|
--
wallet_id: int,
|
|
359
|
--
coin_selection_config: CoinSelectionConfig,
|
|
364
|
++
self, wallet_id: int, coin_selection_config: CoinSelectionConfig
|
|
360
365
|
) -> Tuple[List[CoinRecord], List[CoinRecord], List[Coin]]:
|
|
361
366
|
"""
|
|
362
367
|
We return a tuple containing: (confirmed records, unconfirmed removals, unconfirmed additions)
|
|
363
368
|
"""
|
|
364
|
--
request = {
|
|
365
|
--
|
|
366
|
--
**coin_selection_config.to_json_dict(),
|
|
367
|
--
}
|
|
368
|
--
response: Dict[str, List[Dict]] = await self.fetch("get_spendable_coins", request)
|
|
369
|
++
request = {"wallet_id": wallet_id, **coin_selection_config.to_json_dict()}
|
|
370
|
++
response = await self.fetch("get_spendable_coins", request)
|
|
369
371
|
confirmed_wrs = [CoinRecord.from_json_dict(coin) for coin in response["confirmed_records"]]
|
|
370
372
|
unconfirmed_removals = [CoinRecord.from_json_dict(coin) for coin in response["unconfirmed_removals"]]
|
|
371
373
|
unconfirmed_additions = [Coin.from_json_dict(coin) for coin in response["unconfirmed_additions"]]
|
|
@@@ -377,7 -377,7 +379,7 @@@
|
|
|
377
379
|
include_spent_coins: bool = True,
|
|
378
380
|
start_height: Optional[int] = None,
|
|
379
381
|
end_height: Optional[int] = None,
|
|
380
|
--
) -> List:
|
|
382
|
++
) -> List[CoinRecord]:
|
|
381
383
|
names_hex = [name.hex() for name in names]
|
|
382
384
|
request = {"names": names_hex, "include_spent_coins": include_spent_coins}
|
|
383
385
|
if start_height is not None:
|
|
@@@ -396,8 -396,8 +398,8 @@@
|
|
|
396
398
|
name: Optional[str] = "DID Wallet",
|
|
397
399
|
backup_ids: List[str] = [],
|
|
398
400
|
required_num: int = 0,
|
|
399
|
--
) -> Dict:
|
|
400
|
--
request
|
|
401
|
++
) -> Dict[str, Any]:
|
|
402
|
++
request = {
|
|
401
403
|
"wallet_type": "did_wallet",
|
|
402
404
|
"did_type": "new",
|
|
403
405
|
"backup_dids": backup_ids,
|
|
@@@ -409,26 -409,26 +411,18 @@@
|
|
|
409
411
|
response = await self.fetch("create_new_wallet", request)
|
|
410
412
|
return response
|
|
411
413
|
|
|
412
|
--
async def get_did_id(self, wallet_id: int) -> Dict:
|
|
413
|
--
request
|
|
414
|
--
"wallet_id": wallet_id,
|
|
415
|
--
}
|
|
414
|
++
async def get_did_id(self, wallet_id: int) -> Dict[str, Any]:
|
|
415
|
++
request = {"wallet_id": wallet_id}
|
|
416
416
|
response = await self.fetch("did_get_did", request)
|
|
417
417
|
return response
|
|
418
418
|
|
|
419
|
--
async def get_did_info(self, coin_id: str, latest: bool) -> Dict:
|
|
420
|
--
request:
|
|
421
|
--
"coin_id": coin_id,
|
|
422
|
--
"latest": latest,
|
|
423
|
--
}
|
|
419
|
++
async def get_did_info(self, coin_id: str, latest: bool) -> Dict[str, Any]:
|
|
420
|
++
request = {"coin_id": coin_id, "latest": latest}
|
|
424
421
|
response = await self.fetch("did_get_info", request)
|
|
425
422
|
return response
|
|
426
423
|
|
|
427
|
--
async def create_did_backup_file(self, wallet_id: int, filename: str) -> Dict:
|
|
428
|
--
request:
|
|
429
|
--
"wallet_id": wallet_id,
|
|
430
|
--
"filename": filename,
|
|
431
|
--
}
|
|
424
|
++
async def create_did_backup_file(self, wallet_id: int, filename: str) -> Dict[str, Any]:
|
|
425
|
++
request = {"wallet_id": wallet_id, "filename": filename}
|
|
432
426
|
response = await self.fetch("did_create_backup_file", request)
|
|
433
427
|
return response
|
|
434
428
|
|
|
@@@ -441,7 -441,7 +435,7 @@@
|
|
|
441
435
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
442
436
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
443
437
|
push: bool = True,
|
|
444
|
--
) -> Dict:
|
|
438
|
++
) -> Dict[str, Any]:
|
|
445
439
|
request: Dict[str, Any] = {
|
|
446
440
|
"wallet_id": wallet_id,
|
|
447
441
|
"new_list": recovery_list,
|
|
@@@ -454,10 -454,10 +448,8 @@@
|
|
|
454
448
|
response = await self.fetch("did_update_recovery_ids", request)
|
|
455
449
|
return response
|
|
456
450
|
|
|
457
|
--
async def get_did_recovery_list(self, wallet_id: int) -> Dict:
|
|
458
|
--
request
|
|
459
|
--
"wallet_id": wallet_id,
|
|
460
|
--
}
|
|
451
|
++
async def get_did_recovery_list(self, wallet_id: int) -> Dict[str, Any]:
|
|
452
|
++
request = {"wallet_id": wallet_id}
|
|
461
453
|
response = await self.fetch("did_get_recovery_list", request)
|
|
462
454
|
return response
|
|
463
455
|
|
|
@@@ -468,7 -468,7 +460,7 @@@
|
|
|
468
460
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
469
461
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
470
462
|
push: bool = False,
|
|
471
|
--
) -> Dict:
|
|
463
|
++
) -> Dict[str, Any]:
|
|
472
464
|
request: Dict[str, Any] = {
|
|
473
465
|
"wallet_id": wallet_id,
|
|
474
466
|
"extra_conditions": conditions_to_json_dicts(extra_conditions),
|
|
@@@ -482,12 -482,12 +474,12 @@@
|
|
|
482
474
|
async def update_did_metadata(
|
|
483
475
|
self,
|
|
484
476
|
wallet_id: int,
|
|
485
|
--
metadata: Dict,
|
|
477
|
++
metadata: Dict[str, Any],
|
|
486
478
|
tx_config: TXConfig,
|
|
487
479
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
488
480
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
489
481
|
push: bool = True,
|
|
490
|
--
) -> Dict:
|
|
482
|
++
) -> Dict[str, Any]:
|
|
491
483
|
request: Dict[str, Any] = {
|
|
492
484
|
"wallet_id": wallet_id,
|
|
493
485
|
"metadata": metadata,
|
|
@@@ -499,19 -499,19 +491,19 @@@
|
|
|
499
491
|
response = await self.fetch("did_update_metadata", request)
|
|
500
492
|
return response
|
|
501
493
|
|
|
502
|
--
async def get_did_metadata(self, wallet_id: int) -> Dict:
|
|
503
|
--
request
|
|
504
|
--
"wallet_id": wallet_id,
|
|
505
|
--
}
|
|
494
|
++
async def get_did_metadata(self, wallet_id: int) -> Dict[str, Any]:
|
|
495
|
++
request = {"wallet_id": wallet_id}
|
|
506
496
|
response = await self.fetch("did_get_metadata", request)
|
|
507
497
|
return response
|
|
508
498
|
|
|
509
499
|
async def find_lost_did(
|
|
510
|
--
self,
|
|
511
|
--
|
|
512
|
--
|
|
513
|
--
|
|
514
|
--
|
|
500
|
++
self,
|
|
501
|
++
coin_id: str,
|
|
502
|
++
recovery_list_hash: Optional[str],
|
|
503
|
++
metadata: Optional[Dict[str, Any]],
|
|
504
|
++
num_verification: Optional[int],
|
|
505
|
++
) -> Dict[str, Any]:
|
|
506
|
++
request: Dict[str, Any] = {"coin_id": coin_id}
|
|
515
507
|
if recovery_list_hash is not None:
|
|
516
508
|
request["recovery_list_hash"] = recovery_list_hash
|
|
517
509
|
if metadata is not None:
|
|
@@@ -521,12 -521,12 +513,8 @@@
|
|
|
521
513
|
response = await self.fetch("did_find_lost_did", request)
|
|
522
514
|
return response
|
|
523
515
|
|
|
524
|
--
async def create_new_did_wallet_from_recovery(self, filename: str) -> Dict:
|
|
525
|
--
request:
|
|
526
|
--
"wallet_type": "did_wallet",
|
|
527
|
--
"did_type": "recovery",
|
|
528
|
--
"filename": filename,
|
|
529
|
--
}
|
|
516
|
++
async def create_new_did_wallet_from_recovery(self, filename: str) -> Dict[str, Any]:
|
|
517
|
++
request = {"wallet_type": "did_wallet", "did_type": "recovery", "filename": filename}
|
|
530
518
|
response = await self.fetch("create_new_wallet", request)
|
|
531
519
|
return response
|
|
532
520
|
|
|
@@@ -539,8 -539,8 +527,8 @@@
|
|
|
539
527
|
file_name: str,
|
|
540
528
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
541
529
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
542
|
--
) -> Dict:
|
|
543
|
--
request
|
|
530
|
++
) -> Dict[str, Any]:
|
|
531
|
++
request = {
|
|
544
532
|
"wallet_id": wallet_id,
|
|
545
533
|
"coin_name": coin_name,
|
|
546
534
|
"pubkey": pubkey,
|
|
@@@ -552,11 -552,11 +540,8 @@@
|
|
|
552
540
|
response = await self.fetch("did_create_attest", request)
|
|
553
541
|
return response
|
|
554
542
|
|
|
555
|
--
async def did_recovery_spend(self, wallet_id: int, attest_filenames: str) -> Dict:
|
|
556
|
--
request:
|
|
557
|
--
"wallet_id": wallet_id,
|
|
558
|
--
"attest_filenames": attest_filenames,
|
|
559
|
--
}
|
|
543
|
++
async def did_recovery_spend(self, wallet_id: int, attest_filenames: str) -> Dict[str, Any]:
|
|
544
|
++
request = {"wallet_id": wallet_id, "attest_filenames": attest_filenames}
|
|
560
545
|
response = await self.fetch("did_recovery_spend", request)
|
|
561
546
|
return response
|
|
562
547
|
|
|
@@@ -570,7 -570,7 +555,7 @@@
|
|
|
570
555
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
571
556
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
572
557
|
push: bool = True,
|
|
573
|
--
) -> Dict:
|
|
558
|
++
) -> Dict[str, Any]:
|
|
574
559
|
request: Dict[str, Any] = {
|
|
575
560
|
"wallet_id": wallet_id,
|
|
576
561
|
"inner_address": address,
|
|
@@@ -584,12 -584,12 +569,12 @@@
|
|
|
584
569
|
response = await self.fetch("did_transfer_did", request)
|
|
585
570
|
return response
|
|
586
571
|
|
|
587
|
--
async def did_set_wallet_name(self, wallet_id: int, name: str) -> Dict:
|
|
572
|
++
async def did_set_wallet_name(self, wallet_id: int, name: str) -> Dict[str, Any]:
|
|
588
573
|
request = {"wallet_id": wallet_id, "name": name}
|
|
589
574
|
response = await self.fetch("did_set_wallet_name", request)
|
|
590
575
|
return response
|
|
591
576
|
|
|
592
|
--
async def did_get_wallet_name(self, wallet_id: int) -> Dict:
|
|
577
|
++
async def did_get_wallet_name(self, wallet_id: int) -> Dict[str, Any]:
|
|
593
578
|
request = {"wallet_id": wallet_id}
|
|
594
579
|
response = await self.fetch("did_get_wallet_name", request)
|
|
595
580
|
return response
|
|
@@@ -609,7 -609,7 +594,7 @@@
|
|
|
609
594
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
610
595
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
611
596
|
) -> TransactionRecord:
|
|
612
|
--
request
|
|
597
|
++
request = {
|
|
613
598
|
"wallet_type": "pool_wallet",
|
|
614
599
|
"mode": mode,
|
|
615
600
|
"initial_target_state": {
|
|
@@@ -629,14 -629,14 +614,14 @@@
|
|
|
629
614
|
res = await self.fetch("create_new_wallet", request)
|
|
630
615
|
return TransactionRecord.from_json_dict(res["transaction"])
|
|
631
616
|
|
|
632
|
--
async def pw_self_pool(self, wallet_id: int, fee: uint64) -> Dict:
|
|
617
|
++
async def pw_self_pool(self, wallet_id: int, fee: uint64) -> Dict[str, Any]:
|
|
633
618
|
reply = await self.fetch("pw_self_pool", {"wallet_id": wallet_id, "fee": fee})
|
|
634
619
|
reply = parse_result_transactions(reply)
|
|
635
620
|
return reply
|
|
636
621
|
|
|
637
622
|
async def pw_join_pool(
|
|
638
623
|
self, wallet_id: int, target_puzzlehash: bytes32, pool_url: str, relative_lock_height: uint32, fee: uint64
|
|
639
|
--
) -> Dict:
|
|
624
|
++
) -> Dict[str, Any]:
|
|
640
625
|
request = {
|
|
641
626
|
"wallet_id": int(wallet_id),
|
|
642
627
|
"target_puzzlehash": target_puzzlehash.hex(),
|
|
@@@ -644,14 -644,14 +629,13 @@@
|
|
|
644
629
|
"pool_url": pool_url,
|
|
645
630
|
"fee": fee,
|
|
646
631
|
}
|
|
647
|
--
|
|
648
632
|
reply = await self.fetch("pw_join_pool", request)
|
|
649
633
|
reply = parse_result_transactions(reply)
|
|
650
634
|
return reply
|
|
651
635
|
|
|
652
636
|
async def pw_absorb_rewards(
|
|
653
637
|
self, wallet_id: int, fee: uint64 = uint64(0), max_spends_in_tx: Optional[int] = None
|
|
654
|
--
) -> Dict:
|
|
638
|
++
) -> Dict[str, Any]:
|
|
655
639
|
reply = await self.fetch(
|
|
656
640
|
"pw_absorb_rewards", {"wallet_id": wallet_id, "fee": fee, "max_spends_in_tx": max_spends_in_tx}
|
|
657
641
|
)
|
|
@@@ -667,38 -667,38 +651,27 @@@
|
|
|
667
651
|
)
|
|
668
652
|
|
|
669
653
|
# CATS
|
|
670
|
--
async def create_new_cat_and_wallet(
|
|
671
|
--
|
|
672
|
--
|
|
673
|
--
|
|
674
|
--
"amount": amount,
|
|
675
|
--
"fee": fee,
|
|
676
|
--
"test": test,
|
|
677
|
--
}
|
|
654
|
++
async def create_new_cat_and_wallet(
|
|
655
|
++
self, amount: uint64, fee: uint64 = uint64(0), test: bool = False
|
|
656
|
++
) -> Dict[str, Any]:
|
|
657
|
++
request = {"wallet_type": "cat_wallet", "mode": "new", "amount": amount, "fee": fee, "test": test}
|
|
678
658
|
return await self.fetch("create_new_wallet", request)
|
|
679
659
|
|
|
680
|
--
async def create_wallet_for_existing_cat(self, asset_id: bytes) -> Dict:
|
|
681
|
--
request:
|
|
682
|
--
"wallet_type": "cat_wallet",
|
|
683
|
--
"asset_id": asset_id.hex(),
|
|
684
|
--
"mode": "existing",
|
|
685
|
--
}
|
|
660
|
++
async def create_wallet_for_existing_cat(self, asset_id: bytes) -> Dict[str, Any]:
|
|
661
|
++
request = {"wallet_type": "cat_wallet", "asset_id": asset_id.hex(), "mode": "existing"}
|
|
686
662
|
return await self.fetch("create_new_wallet", request)
|
|
687
663
|
|
|
688
664
|
async def get_cat_asset_id(self, wallet_id: int) -> bytes32:
|
|
689
|
--
request
|
|
690
|
--
"wallet_id": wallet_id,
|
|
691
|
--
}
|
|
665
|
++
request = {"wallet_id": wallet_id}
|
|
692
666
|
return bytes32.from_hexstr((await self.fetch("cat_get_asset_id", request))["asset_id"])
|
|
693
667
|
|
|
694
|
--
async def get_stray_cats(self) -> Dict:
|
|
668
|
++
async def get_stray_cats(self) -> List[Dict[str, Any]]:
|
|
695
669
|
response = await self.fetch("get_stray_cats", {})
|
|
696
|
--
|
|
670
|
++
# TODO: casting due to lack of type checked deserialization
|
|
671
|
++
return cast(List[Dict[str, Any]], response["stray_cats"])
|
|
697
672
|
|
|
698
673
|
async def cat_asset_id_to_name(self, asset_id: bytes32) -> Optional[Tuple[Optional[uint32], str]]:
|
|
699
|
--
request
|
|
700
|
--
"asset_id": asset_id.hex(),
|
|
701
|
--
}
|
|
674
|
++
request = {"asset_id": asset_id.hex()}
|
|
702
675
|
try:
|
|
703
676
|
res = await self.fetch("cat_asset_id_to_name", request)
|
|
704
677
|
except ValueError:
|
|
@@@ -708,10 -708,10 +681,10 @@@
|
|
|
708
681
|
return wallet_id, res["name"]
|
|
709
682
|
|
|
710
683
|
async def get_cat_name(self, wallet_id: int) -> str:
|
|
711
|
--
request
|
|
712
|
--
|
|
713
|
--
|
|
714
|
--
return (
|
|
684
|
++
request = {"wallet_id": wallet_id}
|
|
685
|
++
response = await self.fetch("cat_get_name", request)
|
|
686
|
++
# TODO: casting due to lack of type checked deserialization
|
|
687
|
++
return cast(str, response["name"])
|
|
715
688
|
|
|
716
689
|
async def set_cat_name(self, wallet_id: int, name: str) -> None:
|
|
717
690
|
request: Dict[str, Any] = {
|
|
@@@ -735,10 -735,10 +708,10 @@@
|
|
|
735
708
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
736
709
|
push: bool = True,
|
|
737
710
|
) -> TransactionRecord:
|
|
738
|
--
send_dict
|
|
711
|
++
send_dict = {
|
|
739
712
|
"wallet_id": wallet_id,
|
|
740
713
|
"fee": fee,
|
|
741
|
--
"memos": memos if memos else [],
|
|
714
|
++
"memos": memos if memos is not None else [],
|
|
742
715
|
"extra_conditions": conditions_to_json_dicts(extra_conditions),
|
|
743
716
|
"push": push,
|
|
744
717
|
**tx_config.to_json_dict(),
|
|
@@@ -770,9 -770,9 +743,9 @@@
|
|
|
770
743
|
self,
|
|
771
744
|
offer_dict: Dict[Union[uint32, str], int],
|
|
772
745
|
tx_config: TXConfig,
|
|
773
|
--
driver_dict: Dict[str, Any] = None,
|
|
774
|
--
solver: Dict[str, Any] = None,
|
|
775
|
--
fee=
|
|
746
|
++
driver_dict: Optional[Dict[str, Any]] = None,
|
|
747
|
++
solver: Optional[Dict[str, Any]] = None,
|
|
748
|
++
fee: int = 0,
|
|
776
749
|
validate_only: bool = False,
|
|
777
750
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
778
751
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
@@@ -810,8 -810,8 +783,8 @@@
|
|
|
810
783
|
self,
|
|
811
784
|
offer: Offer,
|
|
812
785
|
tx_config: TXConfig,
|
|
813
|
--
solver: Dict[str, Any] = None,
|
|
814
|
--
fee=
|
|
786
|
++
solver: Optional[Dict[str, Any]] = None,
|
|
787
|
++
fee: int = 0,
|
|
815
788
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
816
789
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
817
790
|
push: bool = True,
|
|
@@@ -838,7 -838,7 +811,7 @@@
|
|
|
838
811
|
self,
|
|
839
812
|
start: int = 0,
|
|
840
813
|
end: int = 50,
|
|
841
|
--
sort_key: str = None,
|
|
814
|
++
sort_key: Optional[str] = None,
|
|
842
815
|
reverse: bool = False,
|
|
843
816
|
file_contents: bool = False,
|
|
844
817
|
exclude_my_offers: bool = False,
|
|
@@@ -873,12 -873,12 +846,12 @@@
|
|
|
873
846
|
self,
|
|
874
847
|
trade_id: bytes32,
|
|
875
848
|
tx_config: TXConfig,
|
|
876
|
--
fee=
|
|
849
|
++
fee: int = 0,
|
|
877
850
|
secure: bool = True,
|
|
878
851
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
879
852
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
880
853
|
push: bool = True,
|
|
881
|
--
):
|
|
854
|
++
) -> None:
|
|
882
855
|
await self.fetch(
|
|
883
856
|
"cancel_offer",
|
|
884
857
|
{
|
|
@@@ -895,7 -895,7 +868,7 @@@
|
|
|
895
868
|
async def cancel_offers(
|
|
896
869
|
self,
|
|
897
870
|
tx_config: TXConfig,
|
|
898
|
--
|
|
871
|
++
batch_fee: int = 0,
|
|
899
872
|
secure: bool = True,
|
|
900
873
|
batch_size: int = 5,
|
|
901
874
|
cancel_all: bool = False,
|
|
@@@ -908,7 -908,7 +881,7 @@@
|
|
|
908
881
|
"cancel_offers",
|
|
909
882
|
{
|
|
910
883
|
"secure": secure,
|
|
911
|
--
"batch_fee":
|
|
884
|
++
"batch_fee": batch_fee,
|
|
912
885
|
"secure": secure,
|
|
913
886
|
"batch_size": batch_size,
|
|
914
887
|
"cancel_all": cancel_all,
|
|
@@@ -921,36 -921,36 +894,32 @@@
|
|
|
921
894
|
)
|
|
922
895
|
|
|
923
896
|
# NFT wallet
|
|
924
|
--
async def create_new_nft_wallet(self, did_id, name=None) ->
|
|
925
|
--
request:
|
|
926
|
--
"wallet_type": "nft_wallet",
|
|
927
|
--
"did_id": did_id,
|
|
928
|
--
"name": name,
|
|
929
|
--
}
|
|
897
|
++
async def create_new_nft_wallet(self, did_id: Optional[str], name: Optional[str] = None) -> Dict[str, Any]:
|
|
898
|
++
request = {"wallet_type": "nft_wallet", "did_id": did_id, "name": name}
|
|
930
899
|
response = await self.fetch("create_new_wallet", request)
|
|
931
900
|
return response
|
|
932
901
|
|
|
933
902
|
async def mint_nft(
|
|
934
903
|
self,
|
|
935
|
--
wallet_id,
|
|
936
|
--
royalty_address,
|
|
937
|
--
target_address,
|
|
938
|
--
hash,
|
|
939
|
--
uris,
|
|
904
|
++
wallet_id: int,
|
|
905
|
++
royalty_address: Optional[str],
|
|
906
|
++
target_address: Optional[str],
|
|
907
|
++
hash: str,
|
|
908
|
++
uris: List[str],
|
|
940
909
|
tx_config: TXConfig,
|
|
941
|
--
meta_hash="",
|
|
942
|
--
meta_uris=[],
|
|
943
|
--
license_hash="",
|
|
944
|
--
license_uris=[],
|
|
945
|
--
edition_total=1,
|
|
946
|
--
edition_number=1,
|
|
947
|
--
fee=0,
|
|
948
|
--
royalty_percentage=0,
|
|
949
|
--
did_id=None,
|
|
910
|
++
meta_hash: Optional[str] = "",
|
|
911
|
++
meta_uris: List[str] = [],
|
|
912
|
++
license_hash: Optional[str] = "",
|
|
913
|
++
license_uris: List[str] = [],
|
|
914
|
++
edition_total: Optional[int] = 1,
|
|
915
|
++
edition_number: Optional[int] = 1,
|
|
916
|
++
fee: int = 0,
|
|
917
|
++
royalty_percentage: int = 0,
|
|
918
|
++
did_id: Optional[str] = None,
|
|
950
919
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
951
920
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
952
921
|
push: bool = True,
|
|
953
|
--
):
|
|
922
|
++
) -> Dict[str, Any]:
|
|
954
923
|
request: Dict[str, Any] = {
|
|
955
924
|
"wallet_id": wallet_id,
|
|
956
925
|
"royalty_address": royalty_address,
|
|
@@@ -976,16 -976,16 +945,16 @@@
|
|
|
976
945
|
|
|
977
946
|
async def add_uri_to_nft(
|
|
978
947
|
self,
|
|
979
|
--
wallet_id,
|
|
980
|
--
nft_coin_id,
|
|
981
|
--
key,
|
|
982
|
--
uri,
|
|
983
|
--
fee,
|
|
948
|
++
wallet_id: int,
|
|
949
|
++
nft_coin_id: str,
|
|
950
|
++
key: str,
|
|
951
|
++
uri: str,
|
|
952
|
++
fee: int,
|
|
984
953
|
tx_config: TXConfig,
|
|
985
954
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
986
955
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
987
956
|
push: bool = True,
|
|
988
|
--
):
|
|
957
|
++
) -> Dict[str, Any]:
|
|
989
958
|
request: Dict[str, Any] = {
|
|
990
959
|
"wallet_id": wallet_id,
|
|
991
960
|
"nft_coin_id": nft_coin_id,
|
|
@@@ -1004,8 -1004,8 +973,8 @@@
|
|
|
1004
973
|
self,
|
|
1005
974
|
royalty_assets_dict: Dict[Any, Tuple[Any, uint16]],
|
|
1006
975
|
fungible_asset_dict: Dict[Any, uint64],
|
|
1007
|
--
) -> Dict[
|
|
1008
|
--
request
|
|
976
|
++
) -> Dict[str, List[Dict[str, Any]]]:
|
|
977
|
++
request = {
|
|
1009
978
|
"royalty_assets": [
|
|
1010
979
|
{"asset": id, "royalty_address": royalty_info[0], "royalty_percentage": royalty_info[1]}
|
|
1011
980
|
for id, royalty_info in royalty_assets_dict.items()
|
|
@@@ -1016,22 -1016,22 +985,22 @@@
|
|
|
1016
985
|
del response["success"]
|
|
1017
986
|
return response
|
|
1018
987
|
|
|
1019
|
--
async def get_nft_info(self, coin_id: str, latest: bool = True):
|
|
1020
|
--
request
|
|
988
|
++
async def get_nft_info(self, coin_id: str, latest: bool = True) -> Dict[str, Any]:
|
|
989
|
++
request = {"coin_id": coin_id, "latest": latest}
|
|
1021
990
|
response = await self.fetch("nft_get_info", request)
|
|
1022
991
|
return response
|
|
1023
992
|
|
|
1024
993
|
async def transfer_nft(
|
|
1025
994
|
self,
|
|
1026
|
--
wallet_id,
|
|
1027
|
--
nft_coin_id,
|
|
1028
|
--
target_address,
|
|
1029
|
--
fee,
|
|
995
|
++
wallet_id: int,
|
|
996
|
++
nft_coin_id: str,
|
|
997
|
++
target_address: str,
|
|
998
|
++
fee: int,
|
|
1030
999
|
tx_config: TXConfig,
|
|
1031
1000
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1032
1001
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
1033
1002
|
push: bool = True,
|
|
1034
|
--
):
|
|
1003
|
++
) -> Dict[str, Any]:
|
|
1035
1004
|
request: Dict[str, Any] = {
|
|
1036
1005
|
"wallet_id": wallet_id,
|
|
1037
1006
|
"nft_coin_id": nft_coin_id,
|
|
@@@ -1045,27 -1045,27 +1014,27 @@@
|
|
|
1045
1014
|
response = await self.fetch("nft_transfer_nft", request)
|
|
1046
1015
|
return response
|
|
1047
1016
|
|
|
1048
|
--
async def count_nfts(self, wallet_id: Optional[int]):
|
|
1049
|
--
request
|
|
1017
|
++
async def count_nfts(self, wallet_id: Optional[int]) -> Dict[str, Any]:
|
|
1018
|
++
request = {"wallet_id": wallet_id}
|
|
1050
1019
|
response = await self.fetch("nft_count_nfts", request)
|
|
1051
1020
|
return response
|
|
1052
1021
|
|
|
1053
|
--
async def list_nfts(self, wallet_id, num: int = 50, start_index: int = 0):
|
|
1054
|
--
request
|
|
1022
|
++
async def list_nfts(self, wallet_id: int, num: int = 50, start_index: int = 0) -> Dict[str, Any]:
|
|
1023
|
++
request = {"wallet_id": wallet_id, "num": num, "start_index": start_index}
|
|
1055
1024
|
response = await self.fetch("nft_get_nfts", request)
|
|
1056
1025
|
return response
|
|
1057
1026
|
|
|
1058
1027
|
async def set_nft_did(
|
|
1059
1028
|
self,
|
|
1060
|
--
wallet_id,
|
|
1061
|
--
did_id,
|
|
1062
|
--
nft_coin_id,
|
|
1063
|
--
fee,
|
|
1029
|
++
wallet_id: int,
|
|
1030
|
++
did_id: str,
|
|
1031
|
++
nft_coin_id: str,
|
|
1032
|
++
fee: int,
|
|
1064
1033
|
tx_config: TXConfig,
|
|
1065
1034
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1066
1035
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
1067
1036
|
push: bool = True,
|
|
1068
|
--
):
|
|
1037
|
++
) -> Dict[str, Any]:
|
|
1069
1038
|
request: Dict[str, Any] = {
|
|
1070
1039
|
"wallet_id": wallet_id,
|
|
1071
1040
|
"did_id": did_id,
|
|
@@@ -1079,8 -1079,8 +1048,8 @@@
|
|
|
1079
1048
|
response = await self.fetch("nft_set_nft_did", request)
|
|
1080
1049
|
return response
|
|
1081
1050
|
|
|
1082
|
--
async def get_nft_wallet_did(self, wallet_id) ->
|
|
1083
|
--
request
|
|
1051
|
++
async def get_nft_wallet_did(self, wallet_id: int) -> Dict[str, Any]:
|
|
1052
|
++
request = {"wallet_id": wallet_id}
|
|
1084
1053
|
response = await self.fetch("nft_get_wallet_did", request)
|
|
1085
1054
|
return response
|
|
1086
1055
|
|
|
@@@ -1094,17 -1094,17 +1063,17 @@@
|
|
|
1094
1063
|
target_list: Optional[List[str]] = None,
|
|
1095
1064
|
mint_number_start: Optional[int] = 1,
|
|
1096
1065
|
mint_total: Optional[int] = None,
|
|
1097
|
--
xch_coins: Optional[List[Dict]] = None,
|
|
1066
|
++
xch_coins: Optional[List[Dict[str, Any]]] = None,
|
|
1098
1067
|
xch_change_target: Optional[str] = None,
|
|
1099
1068
|
new_innerpuzhash: Optional[str] = None,
|
|
1100
|
--
did_coin: Optional[Dict] = None,
|
|
1069
|
++
did_coin: Optional[Dict[str, Any]] = None,
|
|
1101
1070
|
did_lineage_parent: Optional[str] = None,
|
|
1102
1071
|
mint_from_did: Optional[bool] = False,
|
|
1103
1072
|
fee: Optional[int] = 0,
|
|
1104
1073
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1105
1074
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
1106
1075
|
push: bool = False,
|
|
1107
|
--
) -> Dict:
|
|
1076
|
++
) -> Dict[str, Any]:
|
|
1108
1077
|
request = {
|
|
1109
1078
|
"wallet_id": wallet_id,
|
|
1110
1079
|
"metadata_list": metadata_list,
|
|
@@@ -1143,21 -1143,21 +1112,17 @@@
|
|
|
1143
1112
|
**timelock_info.to_json_dict(),
|
|
1144
1113
|
}
|
|
1145
1114
|
response = await self.fetch("create_new_dl", request)
|
|
1146
|
--
txs
|
|
1147
|
--
|
|
1148
|
--
]
|
|
1149
|
--
launcher_id: bytes32 = bytes32.from_hexstr(response["launcher_id"])
|
|
1115
|
++
txs = [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]]
|
|
1116
|
++
launcher_id = bytes32.from_hexstr(response["launcher_id"])
|
|
1150
1117
|
return txs, launcher_id
|
|
1151
1118
|
|
|
1152
1119
|
async def dl_track_new(self, launcher_id: bytes32) -> None:
|
|
1153
1120
|
request = {"launcher_id": launcher_id.hex()}
|
|
1154
1121
|
await self.fetch("dl_track_new", request)
|
|
1155
|
--
return None
|
|
1156
1122
|
|
|
1157
1123
|
async def dl_stop_tracking(self, launcher_id: bytes32) -> None:
|
|
1158
1124
|
request = {"launcher_id": launcher_id.hex()}
|
|
1159
1125
|
await self.fetch("dl_stop_tracking", request)
|
|
1160
|
--
return None
|
|
1161
1126
|
|
|
1162
1127
|
async def dl_latest_singleton(
|
|
1163
1128
|
self, launcher_id: bytes32, only_confirmed: bool = False
|
|
@@@ -1195,9 -1195,9 +1160,7 @@@
|
|
|
1195
1160
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1196
1161
|
timelock_info: ConditionValidTimes = ConditionValidTimes(),
|
|
1197
1162
|
) -> List[TransactionRecord]:
|
|
1198
|
--
updates_as_strings:
|
|
1199
|
--
for lid, root in update_dictionary.items():
|
|
1200
|
--
updates_as_strings[str(lid)] = str(root)
|
|
1163
|
++
updates_as_strings = {str(lid): str(root) for lid, root in update_dictionary.items()}
|
|
1201
1164
|
request = {
|
|
1202
1165
|
"updates": updates_as_strings,
|
|
1203
1166
|
"extra_conditions": conditions_to_json_dicts(extra_conditions),
|
|
@@@ -1296,11 -1296,11 +1259,13 @@@
|
|
|
1296
1259
|
]
|
|
1297
1260
|
|
|
1298
1261
|
async def delete_notifications(self, ids: Optional[List[bytes32]] = None) -> bool:
|
|
1299
|
--
request
|
|
1262
|
++
request = {}
|
|
1300
1263
|
if ids is not None:
|
|
1301
1264
|
request["ids"] = [id.hex() for id in ids]
|
|
1302
1265
|
response = await self.fetch("delete_notifications", request)
|
|
1303
|
--
|
|
1266
|
++
# TODO: casting due to lack of type checked deserialization
|
|
1267
|
++
result = cast(bool, response["success"])
|
|
1268
|
++
return result
|
|
1304
1269
|
|
|
1305
1270
|
async def send_notification(
|
|
1306
1271
|
self,
|
|
@@@ -1345,8 -1345,8 +1310,8 @@@
|
|
|
1345
1310
|
fee: uint64 = uint64(0),
|
|
1346
1311
|
fee_for_cat: uint64 = uint64(0),
|
|
1347
1312
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1348
|
--
) -> Dict:
|
|
1349
|
--
request
|
|
1313
|
++
) -> Dict[str, Any]:
|
|
1314
|
++
request = {
|
|
1350
1315
|
"wallet_type": "dao_wallet",
|
|
1351
1316
|
"mode": mode,
|
|
1352
1317
|
"treasury_id": treasury_id,
|
|
@@@ -1362,19 -1362,19 +1327,13 @@@
|
|
|
1362
1327
|
response = await self.fetch("create_new_wallet", request)
|
|
1363
1328
|
return response
|
|
1364
1329
|
|
|
1365
|
--
async def dao_get_treasury_id(
|
|
1366
|
--
|
|
1367
|
--
wallet_id: int,
|
|
1368
|
--
) -> Dict:
|
|
1369
|
--
request: Dict[str, Any] = {"wallet_id": wallet_id}
|
|
1330
|
++
async def dao_get_treasury_id(self, wallet_id: int) -> Dict[str, Any]:
|
|
1331
|
++
request = {"wallet_id": wallet_id}
|
|
1370
1332
|
response = await self.fetch("dao_get_treasury_id", request)
|
|
1371
1333
|
return response
|
|
1372
1334
|
|
|
1373
|
--
async def dao_get_rules(
|
|
1374
|
--
|
|
1375
|
--
wallet_id: int,
|
|
1376
|
--
) -> Dict:
|
|
1377
|
--
request: Dict[str, Any] = {"wallet_id": wallet_id}
|
|
1335
|
++
async def dao_get_rules(self, wallet_id: int) -> Dict[str, Any]:
|
|
1336
|
++
request = {"wallet_id": wallet_id}
|
|
1378
1337
|
response = await self.fetch("dao_get_rules", request)
|
|
1379
1338
|
return response
|
|
1380
1339
|
|
|
@@@ -1383,7 -1383,7 +1342,7 @@@
|
|
|
1383
1342
|
wallet_id: int,
|
|
1384
1343
|
proposal_type: str,
|
|
1385
1344
|
tx_config: TXConfig,
|
|
1386
|
--
additions: Optional[List[Dict]] = None,
|
|
1345
|
++
additions: Optional[List[Dict[str, Any]]] = None,
|
|
1387
1346
|
amount: Optional[uint64] = None,
|
|
1388
1347
|
inner_address: Optional[str] = None,
|
|
1389
1348
|
asset_id: Optional[str] = None,
|
|
@@@ -1392,8 -1392,8 +1351,8 @@@
|
|
|
1392
1351
|
new_dao_rules: Optional[Dict[str, Optional[uint64]]] = None,
|
|
1393
1352
|
fee: uint64 = uint64(0),
|
|
1394
1353
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1395
|
--
) -> Dict:
|
|
1396
|
--
request
|
|
1354
|
++
) -> Dict[str, Any]:
|
|
1355
|
++
request = {
|
|
1397
1356
|
"wallet_id": wallet_id,
|
|
1398
1357
|
"proposal_type": proposal_type,
|
|
1399
1358
|
"additions": additions,
|
|
@@@ -1411,13 -1411,13 +1370,13 @@@
|
|
|
1411
1370
|
response = await self.fetch("dao_create_proposal", request)
|
|
1412
1371
|
return response
|
|
1413
1372
|
|
|
1414
|
--
async def dao_get_proposal_state(self, wallet_id: int, proposal_id: str):
|
|
1415
|
--
request
|
|
1373
|
++
async def dao_get_proposal_state(self, wallet_id: int, proposal_id: str) -> Dict[str, Any]:
|
|
1374
|
++
request = {"wallet_id": wallet_id, "proposal_id": proposal_id}
|
|
1416
1375
|
response = await self.fetch("dao_get_proposal_state", request)
|
|
1417
1376
|
return response
|
|
1418
1377
|
|
|
1419
|
--
async def dao_parse_proposal(self, wallet_id: int, proposal_id: str):
|
|
1420
|
--
request
|
|
1378
|
++
async def dao_parse_proposal(self, wallet_id: int, proposal_id: str) -> Dict[str, Any]:
|
|
1379
|
++
request = {"wallet_id": wallet_id, "proposal_id": proposal_id}
|
|
1421
1380
|
response = await self.fetch("dao_parse_proposal", request)
|
|
1422
1381
|
return response
|
|
1423
1382
|
|
|
@@@ -1430,8 -1430,8 +1389,8 @@@
|
|
|
1430
1389
|
is_yes_vote: bool = True,
|
|
1431
1390
|
fee: uint64 = uint64(0),
|
|
1432
1391
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1433
|
--
):
|
|
1434
|
--
request
|
|
1392
|
++
) -> Dict[str, Any]:
|
|
1393
|
++
request = {
|
|
1435
1394
|
"wallet_id": wallet_id,
|
|
1436
1395
|
"proposal_id": proposal_id,
|
|
1437
1396
|
"vote_amount": vote_amount,
|
|
@@@ -1443,8 -1443,8 +1402,8 @@@
|
|
|
1443
1402
|
response = await self.fetch("dao_vote_on_proposal", request)
|
|
1444
1403
|
return response
|
|
1445
1404
|
|
|
1446
|
--
async def dao_get_proposals(self, wallet_id: int, include_closed: bool = True):
|
|
1447
|
--
request
|
|
1405
|
++
async def dao_get_proposals(self, wallet_id: int, include_closed: bool = True) -> Dict[str, Any]:
|
|
1406
|
++
request = {"wallet_id": wallet_id, "include_closed": include_closed}
|
|
1448
1407
|
response = await self.fetch("dao_get_proposals", request)
|
|
1449
1408
|
return response
|
|
1450
1409
|
|
|
@@@ -1453,11 -1453,11 +1412,11 @@@
|
|
|
1453
1412
|
wallet_id: int,
|
|
1454
1413
|
proposal_id: str,
|
|
1455
1414
|
tx_config: TXConfig,
|
|
1456
|
--
self_destruct: bool = None,
|
|
1415
|
++
self_destruct: Optional[bool] = None,
|
|
1457
1416
|
fee: uint64 = uint64(0),
|
|
1458
1417
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1459
|
--
):
|
|
1460
|
--
request
|
|
1418
|
++
) -> Dict[str, Any]:
|
|
1419
|
++
request = {
|
|
1461
1420
|
"wallet_id": wallet_id,
|
|
1462
1421
|
"proposal_id": proposal_id,
|
|
1463
1422
|
"self_destruct": self_destruct,
|
|
@@@ -1474,8 -1474,8 +1433,8 @@@
|
|
|
1474
1433
|
tx_config: TXConfig,
|
|
1475
1434
|
fee: uint64 = uint64(0),
|
|
1476
1435
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1477
|
--
):
|
|
1478
|
--
request
|
|
1436
|
++
) -> Dict[str, Any]:
|
|
1437
|
++
request = {
|
|
1479
1438
|
"wallet_id": wallet_id,
|
|
1480
1439
|
"fee": fee,
|
|
1481
1440
|
"extra_conditions": list(extra_conditions),
|
|
@@@ -1484,8 -1484,8 +1443,8 @@@
|
|
|
1484
1443
|
response = await self.fetch("dao_free_coins_from_finished_proposals", request)
|
|
1485
1444
|
return response
|
|
1486
1445
|
|
|
1487
|
--
async def dao_get_treasury_balance(self, wallet_id: int):
|
|
1488
|
--
request
|
|
1446
|
++
async def dao_get_treasury_balance(self, wallet_id: int) -> Dict[str, Any]:
|
|
1447
|
++
request = {"wallet_id": wallet_id}
|
|
1489
1448
|
response = await self.fetch("dao_get_treasury_balance", request)
|
|
1490
1449
|
return response
|
|
1491
1450
|
|
|
@@@ -1497,8 -1497,8 +1456,8 @@@
|
|
|
1497
1456
|
tx_config: TXConfig,
|
|
1498
1457
|
fee: uint64 = uint64(0),
|
|
1499
1458
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1500
|
--
):
|
|
1501
|
--
request
|
|
1459
|
++
) -> Dict[str, Any]:
|
|
1460
|
++
request = {
|
|
1502
1461
|
"wallet_id": wallet_id,
|
|
1503
1462
|
"funding_wallet_id": funding_wallet_id,
|
|
1504
1463
|
"amount": amount,
|
|
@@@ -1516,8 -1516,8 +1475,8 @@@
|
|
|
1516
1475
|
tx_config: TXConfig,
|
|
1517
1476
|
fee: uint64 = uint64(0),
|
|
1518
1477
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1519
|
--
):
|
|
1520
|
--
request
|
|
1478
|
++
) -> Dict[str, Any]:
|
|
1479
|
++
request = {
|
|
1521
1480
|
"wallet_id": wallet_id,
|
|
1522
1481
|
"amount": amount,
|
|
1523
1482
|
"fee": fee,
|
|
@@@ -1531,11 -1531,11 +1490,11 @@@
|
|
|
1531
1490
|
self,
|
|
1532
1491
|
wallet_id: int,
|
|
1533
1492
|
tx_config: TXConfig,
|
|
1534
|
--
coins: Optional[List[Dict]] = None,
|
|
1493
|
++
coins: Optional[List[Dict[str, Any]]] = None,
|
|
1535
1494
|
fee: uint64 = uint64(0),
|
|
1536
1495
|
extra_conditions: Tuple[Condition, ...] = tuple(),
|
|
1537
|
--
):
|
|
1538
|
--
request
|
|
1496
|
++
) -> Dict[str, Any]:
|
|
1497
|
++
request = {
|
|
1539
1498
|
"wallet_id": wallet_id,
|
|
1540
1499
|
"coins": coins,
|
|
1541
1500
|
"fee": fee,
|
|
@@@ -1545,8 -1545,8 +1504,8 @@@
|
|
|
1545
1504
|
response = await self.fetch("dao_exit_lockup", request)
|
|
1546
1505
|
return response
|
|
1547
1506
|
|
|
1548
|
--
async def dao_adjust_filter_level(self, wallet_id: int, filter_level: int):
|
|
1549
|
--
request
|
|
1507
|
++
async def dao_adjust_filter_level(self, wallet_id: int, filter_level: int) -> Dict[str, Any]:
|
|
1508
|
++
request = {"wallet_id": wallet_id, "filter_level": filter_level}
|
|
1550
1509
|
response = await self.fetch("dao_adjust_filter_level", request)
|
|
1551
1510
|
return response
|
|
1552
1511
|
|
|
@@@ -1619,7 -1619,7 +1578,7 @@@
|
|
|
1619
1578
|
|
|
1620
1579
|
async def vc_get_proofs_for_root(self, root: bytes32) -> Dict[str, Any]:
|
|
1621
1580
|
response = await self.fetch("vc_get_proofs_for_root", {"root": root.hex()})
|
|
1622
|
--
return response["proofs"]
|
|
1581
|
++
return cast(Dict[str, Any], response["proofs"])
|
|
1623
1582
|
|
|
1624
1583
|
async def vc_revoke(
|
|
1625
1584
|
self,
|
|
@@@ -981,8 -981,8 +981,8 @@@ class BlockTools
|
|
|
981
981
|
pending_ses = True
|
|
982
982
|
ses_hash: Optional[bytes32] = sub_epoch_summary.get_hash()
|
|
983
983
|
# if the last block is the last block of the epoch, we set the new sub-slot iters and difficulty
|
|
984
|
--
new_sub_slot_iters: Optional[uint64] = sub_epoch_summary.new_sub_slot_iters
|
|
985
|
--
new_difficulty: Optional[uint64] = sub_epoch_summary.new_difficulty
|
|
984
|
++
new_sub_slot_iters: Optional[uint64] = uint64.construct_optional(sub_epoch_summary.new_sub_slot_iters)
|
|
985
|
++
new_difficulty: Optional[uint64] = uint64.construct_optional(sub_epoch_summary.new_difficulty)
|
|
986
986
|
|
|
987
987
|
self.log.info(f"Sub epoch summary: {sub_epoch_summary} for block {latest_block.height+1}")
|
|
988
988
|
else: # the previous block is not the last block of the sub-epoch or epoch
|
|
@@@ -1252,8 -1252,8 +1252,8 @@@
|
|
|
1252
1252
|
num_empty_slots_added += 1
|
|
1253
1253
|
|
|
1254
1254
|
if new_sub_slot_iters is not None and new_difficulty is not None: # new epoch
|
|
1255
|
--
sub_slot_iters = new_sub_slot_iters
|
|
1256
|
--
difficulty = new_difficulty
|
|
1255
|
++
sub_slot_iters = uint64(new_sub_slot_iters)
|
|
1256
|
++
difficulty = uint64(new_difficulty)
|
|
1257
1257
|
|
|
1258
1258
|
def create_genesis_block(
|
|
1259
1259
|
self,
|
|
@@@ -1350,7 -1350,7 +1350,7 @@@
|
|
|
1350
1350
|
cc_challenge,
|
|
1351
1351
|
ip_iters,
|
|
1352
1352
|
)
|
|
1353
|
--
cc_ip_vdf = replace(
|
|
1353
|
++
cc_ip_vdf = cc_ip_vdf.replace(number_of_iterations=ip_iters)
|
|
1354
1354
|
rc_ip_vdf, rc_ip_proof = get_vdf_info_and_proof(
|
|
1355
1355
|
constants,
|
|
1356
1356
|
ClassgroupElement.get_default_element(),
|
|
@@@ -1421,7 -1421,7 +1421,7 @@@
|
|
|
1421
1421
|
+ calculate_sp_iters(
|
|
1422
1422
|
self.constants,
|
|
1423
1423
|
self.constants.SUB_SLOT_ITERS_STARTING,
|
|
1424
|
--
unfinished_block.reward_chain_block.signage_point_index,
|
|
1424
|
++
uint8(unfinished_block.reward_chain_block.signage_point_index),
|
|
1425
1425
|
)
|
|
1426
1426
|
)
|
|
1427
1427
|
return unfinished_block_to_full_block(
|
|
@@@ -1552,7 -1552,7 +1552,7 @@@ def get_signage_point
|
|
|
1552
1552
|
rc_vdf_challenge,
|
|
1553
1553
|
rc_vdf_iters,
|
|
1554
1554
|
)
|
|
1555
|
--
cc_sp_vdf = replace(
|
|
1555
|
++
cc_sp_vdf = cc_sp_vdf.replace(number_of_iterations=sp_iters)
|
|
1556
1556
|
if normalized_to_identity_cc_sp:
|
|
1557
1557
|
_, cc_sp_proof = get_vdf_info_and_proof(
|
|
1558
1558
|
constants,
|
|
@@@ -1597,7 -1597,7 +1597,7 @@@ def finish_block
|
|
|
1597
1597
|
cc_vdf_challenge,
|
|
1598
1598
|
new_ip_iters,
|
|
1599
1599
|
)
|
|
1600
|
--
cc_ip_vdf = replace(
|
|
1600
|
++
cc_ip_vdf = cc_ip_vdf.replace(number_of_iterations=ip_iters)
|
|
1601
1601
|
if normalized_to_identity_cc_ip:
|
|
1602
1602
|
_, cc_ip_proof = get_vdf_info_and_proof(
|
|
1603
1603
|
constants,
|
|
@@@ -1750,7 -1750,7 +1750,7 @@@ def get_icc
|
|
|
1750
1750
|
if len(finished_sub_slots) == 0:
|
|
1751
1751
|
prev_deficit = latest_block.deficit
|
|
1752
1752
|
else:
|
|
1753
|
--
prev_deficit = finished_sub_slots[-1].reward_chain.deficit
|
|
1753
|
++
prev_deficit = uint8(finished_sub_slots[-1].reward_chain.deficit)
|
|
1754
1754
|
|
|
1755
1755
|
if deficit == prev_deficit == constants.MIN_BLOCKS_PER_CHALLENGE_BLOCK:
|
|
1756
1756
|
# new slot / overflow sb to new slot / overflow sb
|
|
@@@ -2051,7 -2051,7 +2051,7 @@@ def create_block_tools
|
|
|
2051
2051
|
|
|
2052
2052
|
|
|
2053
2053
|
def make_unfinished_block(block: FullBlock, constants: ConsensusConstants) -> UnfinishedBlock:
|
|
2054
|
--
if is_overflow_block(constants, block.reward_chain_block.signage_point_index):
|
|
2054
|
++
if is_overflow_block(constants, uint8(block.reward_chain_block.signage_point_index)):
|
|
2055
2055
|
finished_ss = block.finished_sub_slots[:-1]
|
|
2056
2056
|
else:
|
|
2057
2057
|
finished_ss = block.finished_sub_slots
|
|
@@@ -7,7 -7,7 +7,7 @@@ from chia.consensus.pot_iterations impo
|
|
|
7
7
|
from chia.types.blockchain_format.proof_of_space import verify_and_get_quality_string
|
|
8
8
|
from chia.types.blockchain_format.reward_chain_block import RewardChainBlock, RewardChainBlockUnfinished
|
|
9
9
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
10
|
--
from chia.util.ints import uint32, uint64
|
|
10
|
++
from chia.util.ints import uint8, uint32, uint64
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def iters_from_block(
|
|
@@@ -40,11 -40,11 +40,11 @@@
|
|
|
40
40
|
cc_sp,
|
|
41
41
|
)
|
|
42
42
|
return (
|
|
43
|
--
calculate_sp_iters(constants, sub_slot_iters, reward_chain_block.signage_point_index),
|
|
43
|
++
calculate_sp_iters(constants, sub_slot_iters, uint8(reward_chain_block.signage_point_index)),
|
|
44
44
|
calculate_ip_iters(
|
|
45
45
|
constants,
|
|
46
46
|
sub_slot_iters,
|
|
47
|
--
reward_chain_block.signage_point_index,
|
|
47
|
++
uint8(reward_chain_block.signage_point_index),
|
|
48
48
|
required_iters,
|
|
49
49
|
),
|
|
50
50
|
)
|
|
@@@ -256,7 -256,7 +256,7 @@@ class Timelord
|
|
|
256
256
|
log.warning(f"Received invalid unfinished block: {e}.")
|
|
257
257
|
return None
|
|
258
258
|
block_sp_total_iters = self.last_state.total_iters - ip_iters + block_sp_iters
|
|
259
|
--
if is_overflow_block(self.constants, block.reward_chain_block.signage_point_index):
|
|
259
|
++
if is_overflow_block(self.constants, uint8(block.reward_chain_block.signage_point_index)):
|
|
260
260
|
block_sp_total_iters -= self.last_state.get_sub_slot_iters()
|
|
261
261
|
found_index = -1
|
|
262
262
|
for index, (rc, total_iters) in enumerate(self.last_state.reward_challenge_cache):
|
|
@@@ -279,7 -279,7 +279,7 @@@
|
|
|
279
279
|
)
|
|
280
280
|
return None
|
|
281
281
|
if self.last_state.reward_challenge_cache[found_index][1] > block_sp_total_iters:
|
|
282
|
--
if not is_overflow_block(self.constants, block.reward_chain_block.signage_point_index):
|
|
282
|
++
if not is_overflow_block(self.constants, uint8(block.reward_chain_block.signage_point_index)):
|
|
283
283
|
log.error(
|
|
284
284
|
f"Will not infuse unfinished block {block.rc_prev}, sp total iters: {block_sp_total_iters}, "
|
|
285
285
|
f"because its iters are too low"
|
|
@@@ -485,13 -485,13 +485,13 @@@
|
|
|
485
485
|
rc_challenge = self.last_state.get_challenge(Chain.REWARD_CHAIN)
|
|
486
486
|
if rc_info.challenge != rc_challenge:
|
|
487
487
|
assert rc_challenge is not None
|
|
488
|
--
log.warning(f"SP: Do not have correct challenge {rc_challenge.hex()} has {rc_info.challenge}")
|
|
488
|
++
log.warning(f"SP: Do not have correct challenge {rc_challenge.hex()} has {rc_info.challenge.hex()}")
|
|
489
489
|
# This proof is on an outdated challenge, so don't use it
|
|
490
490
|
continue
|
|
491
491
|
iters_from_sub_slot_start = uint64(cc_info.number_of_iterations + self.last_state.get_last_ip())
|
|
492
492
|
response = timelord_protocol.NewSignagePointVDF(
|
|
493
493
|
signage_point_index,
|
|
494
|
--
|
|
494
|
++
cc_info.replace(number_of_iterations=iters_from_sub_slot_start),
|
|
495
495
|
cc_proof,
|
|
496
496
|
rc_info,
|
|
497
497
|
rc_proof,
|
|
@@@ -584,7 -584,7 +584,7 @@@
|
|
|
584
584
|
assert rc_challenge is not None
|
|
585
585
|
log.warning(
|
|
586
586
|
f"Do not have correct challenge {rc_challenge.hex()} "
|
|
587
|
--
f"has {rc_info.challenge}, partial hash {block.reward_chain_block.get_hash()}"
|
|
587
|
++
f"has {rc_info.challenge.hex()}, partial hash {block.reward_chain_block.get_hash()}"
|
|
588
588
|
)
|
|
589
589
|
# This proof is on an outdated challenge, so don't use it
|
|
590
590
|
continue
|
|
@@@ -593,13 -593,13 +593,13 @@@
|
|
|
593
593
|
self.last_active_time = time.time()
|
|
594
594
|
log.debug(f"Generated infusion point for challenge: {challenge} iterations: {iteration}.")
|
|
595
595
|
|
|
596
|
--
overflow = is_overflow_block(self.constants, block.reward_chain_block.signage_point_index)
|
|
596
|
++
overflow = is_overflow_block(self.constants, uint8(block.reward_chain_block.signage_point_index))
|
|
597
597
|
|
|
598
598
|
if not self.last_state.can_infuse_block(overflow):
|
|
599
599
|
log.warning("Too many blocks, or overflow in new epoch, cannot infuse, discarding")
|
|
600
600
|
return
|
|
601
601
|
|
|
602
|
--
cc_info =
|
|
602
|
++
cc_info = cc_info.replace(number_of_iterations=ip_iters)
|
|
603
603
|
response = timelord_protocol.NewInfusionPointVDF(
|
|
604
604
|
challenge,
|
|
605
605
|
cc_info,
|
|
@@@ -628,7 -628,7 +628,7 @@@
|
|
|
628
628
|
+ calculate_sp_iters(
|
|
629
629
|
self.constants,
|
|
630
630
|
block.sub_slot_iters,
|
|
631
|
--
block.reward_chain_block.signage_point_index,
|
|
631
|
++
uint8(block.reward_chain_block.signage_point_index),
|
|
632
632
|
)
|
|
633
633
|
- (block.sub_slot_iters if overflow else 0)
|
|
634
634
|
)
|
|
@@@ -755,14 -755,14 +755,14 @@@
|
|
|
755
755
|
rc_challenge = self.last_state.get_challenge(Chain.REWARD_CHAIN)
|
|
756
756
|
if rc_vdf.challenge != rc_challenge:
|
|
757
757
|
assert rc_challenge is not None
|
|
758
|
--
log.warning(f"Do not have correct challenge {rc_challenge.hex()} has {rc_vdf.challenge}")
|
|
758
|
++
log.warning(f"Do not have correct challenge {rc_challenge.hex()} has {rc_vdf.challenge.hex()}")
|
|
759
759
|
# This proof is on an outdated challenge, so don't use it
|
|
760
760
|
return
|
|
761
761
|
log.debug("Collected end of subslot vdfs.")
|
|
762
762
|
self.iters_finished.add(iter_to_look_for)
|
|
763
763
|
self.last_active_time = time.time()
|
|
764
764
|
iters_from_sub_slot_start = uint64(cc_vdf.number_of_iterations + self.last_state.get_last_ip())
|
|
765
|
--
cc_vdf =
|
|
765
|
++
cc_vdf = cc_vdf.replace(number_of_iterations=iters_from_sub_slot_start)
|
|
766
766
|
if icc_ip_vdf is not None:
|
|
767
767
|
if self.last_state.peak is not None:
|
|
768
768
|
total_iters = (
|
|
@@@ -778,7 -778,7 +778,7 @@@
|
|
|
778
778
|
log.error(f"{self.last_state.subslot_end}")
|
|
779
779
|
assert False
|
|
780
780
|
assert iters_from_cb <= self.last_state.sub_slot_iters
|
|
781
|
--
icc_ip_vdf =
|
|
781
|
++
icc_ip_vdf = icc_ip_vdf.replace(number_of_iterations=iters_from_cb)
|
|
782
782
|
|
|
783
783
|
icc_sub_slot: Optional[InfusedChallengeChainSubSlot] = (
|
|
784
784
|
None if icc_ip_vdf is None else InfusedChallengeChainSubSlot(icc_ip_vdf)
|
|
@@@ -1118,7 -1118,7 +1118,7 @@@
|
|
|
1118
1118
|
ip,
|
|
1119
1119
|
reader,
|
|
1120
1120
|
writer,
|
|
1121
|
--
info[1].new_proof_of_time.number_of_iterations,
|
|
1121
|
++
uint64(info[1].new_proof_of_time.number_of_iterations),
|
|
1122
1122
|
info[1].header_hash,
|
|
1123
1123
|
info[1].height,
|
|
1124
1124
|
info[1].field_vdf,
|
|
@@@ -1170,7 -1170,7 +1170,7 @@@
|
|
|
1170
1170
|
bluebox_process_data = BlueboxProcessData(
|
|
1171
1171
|
picked_info.new_proof_of_time.challenge,
|
|
1172
1172
|
uint16(self.constants.DISCRIMINANT_SIZE_BITS),
|
|
1173
|
--
picked_info.new_proof_of_time.number_of_iterations,
|
|
1173
|
++
uint64(picked_info.new_proof_of_time.number_of_iterations),
|
|
1174
1174
|
)
|
|
1175
1175
|
proof = await asyncio.get_running_loop().run_in_executor(
|
|
1176
1176
|
pool,
|
|
@@@ -59,13 -59,13 +59,13 @@@ class LastState
|
|
|
59
59
|
state.reward_chain_block,
|
|
60
60
|
state.sub_slot_iters,
|
|
61
61
|
state.difficulty,
|
|
62
|
--
state.reward_chain_block.height,
|
|
62
|
++
uint32(state.reward_chain_block.height),
|
|
63
63
|
)
|
|
64
64
|
self.deficit = state.deficit
|
|
65
65
|
self.sub_epoch_summary = state.sub_epoch_summary
|
|
66
|
--
self.last_weight = state.reward_chain_block.weight
|
|
67
|
--
self.last_height = state.reward_chain_block.height
|
|
68
|
--
self.total_iters = state.reward_chain_block.total_iters
|
|
66
|
++
self.last_weight = uint128(state.reward_chain_block.weight)
|
|
67
|
++
self.last_height = uint32(state.reward_chain_block.height)
|
|
68
|
++
self.total_iters = uint128(state.reward_chain_block.total_iters)
|
|
69
69
|
self.last_peak_challenge = state.reward_chain_block.get_hash()
|
|
70
70
|
self.difficulty = state.difficulty
|
|
71
71
|
self.sub_slot_iters = state.sub_slot_iters
|
|
@@@ -87,11 -87,11 +87,11 @@@
|
|
|
87
87
|
self.peak = None
|
|
88
88
|
self.subslot_end = state
|
|
89
89
|
self.last_ip = uint64(0)
|
|
90
|
--
self.deficit = state.reward_chain.deficit
|
|
90
|
++
self.deficit = uint8(state.reward_chain.deficit)
|
|
91
91
|
if state.challenge_chain.new_difficulty is not None:
|
|
92
92
|
assert state.challenge_chain.new_sub_slot_iters is not None
|
|
93
|
--
self.difficulty = state.challenge_chain.new_difficulty
|
|
94
|
--
self.sub_slot_iters = state.challenge_chain.new_sub_slot_iters
|
|
93
|
++
self.difficulty = uint64(state.challenge_chain.new_difficulty)
|
|
94
|
++
self.sub_slot_iters = uint64(state.challenge_chain.new_sub_slot_iters)
|
|
95
95
|
self.new_epoch = True
|
|
96
96
|
else:
|
|
97
97
|
self.new_epoch = False
|
|
@@@ -1,34 -1,34 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
from
|
|
3
|
++
from chia_rs import ClassgroupElement
|
|
4
4
|
|
|
5
|
--
|
|
6
|
--
from chia.util.streamable import Streamable, streamable
|
|
7
|
--
|
|
8
|
--
|
|
9
|
--
@streamable
|
|
10
|
--
@dataclass(frozen=True)
|
|
11
|
--
class ClassgroupElement(Streamable):
|
|
12
|
--
"""
|
|
13
|
--
Represents a classgroup element (a,b,c) where a, b, and c are 512 bit signed integers. However this is using
|
|
14
|
--
a compressed representation. VDF outputs are a single classgroup element. VDF proofs can also be one classgroup
|
|
15
|
--
element (or multiple).
|
|
16
|
--
"""
|
|
17
|
--
|
|
18
|
--
data: bytes100
|
|
19
|
--
|
|
20
|
--
@staticmethod
|
|
21
|
--
def create(data: bytes) -> ClassgroupElement:
|
|
22
|
--
if len(data) < 100:
|
|
23
|
--
data += b"\x00" * (100 - len(data))
|
|
24
|
--
return ClassgroupElement(bytes100(data))
|
|
25
|
--
|
|
26
|
--
@staticmethod
|
|
27
|
--
def get_default_element() -> ClassgroupElement:
|
|
28
|
--
# Bit 3 in the first byte of serialized compressed form indicates if
|
|
29
|
--
# it's the default generator element.
|
|
30
|
--
return ClassgroupElement.create(b"\x08")
|
|
31
|
--
|
|
32
|
--
@staticmethod
|
|
33
|
--
def get_size() -> int:
|
|
34
|
--
return 100
|
|
5
|
++
__all__ = ["ClassgroupElement"]
|
|
@@@ -1,61 -1,61 +1,8 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
|
|
4
|
--
from typing import List, Optional
|
|
3
|
++
import chia_rs
|
|
5
4
|
|
|
6
|
--
|
|
7
|
--
|
|
8
|
--
|
|
9
|
--
|
|
10
|
--
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
11
|
--
from chia.util.ints import uint64
|
|
12
|
--
from chia.util.streamable import Streamable, streamable
|
|
13
|
--
|
|
14
|
--
|
|
15
|
--
@streamable
|
|
16
|
--
@dataclass(frozen=True)
|
|
17
|
--
class TransactionsInfo(Streamable):
|
|
18
|
--
# Information that goes along with each transaction block
|
|
19
|
--
generator_root: bytes32 # sha256 of the block generator in this block
|
|
20
|
--
generator_refs_root: bytes32 # sha256 of the concatenation of the generator ref list entries
|
|
21
|
--
aggregated_signature: G2Element
|
|
22
|
--
fees: uint64 # This only includes user fees, not block rewards
|
|
23
|
--
cost: uint64 # This is the total cost of this block, including CLVM cost, cost of program size and conditions
|
|
24
|
--
reward_claims_incorporated: List[Coin] # These can be in any order
|
|
25
|
--
|
|
26
|
--
|
|
27
|
--
@streamable
|
|
28
|
--
@dataclass(frozen=True)
|
|
29
|
--
class FoliageTransactionBlock(Streamable):
|
|
30
|
--
# Information that goes along with each transaction block that is relevant for light clients
|
|
31
|
--
prev_transaction_block_hash: bytes32
|
|
32
|
--
timestamp: uint64
|
|
33
|
--
filter_hash: bytes32
|
|
34
|
--
additions_root: bytes32
|
|
35
|
--
removals_root: bytes32
|
|
36
|
--
transactions_info_hash: bytes32
|
|
37
|
--
|
|
38
|
--
|
|
39
|
--
@streamable
|
|
40
|
--
@dataclass(frozen=True)
|
|
41
|
--
class FoliageBlockData(Streamable):
|
|
42
|
--
# Part of the block that is signed by the plot key
|
|
43
|
--
unfinished_reward_block_hash: bytes32
|
|
44
|
--
pool_target: PoolTarget
|
|
45
|
--
pool_signature: Optional[G2Element] # Iff ProofOfSpace has a pool pk
|
|
46
|
--
farmer_reward_puzzle_hash: bytes32
|
|
47
|
--
extension_data: bytes32 # Used for future updates. Can be any 32 byte value initially
|
|
48
|
--
|
|
49
|
--
|
|
50
|
--
@streamable
|
|
51
|
--
@dataclass(frozen=True)
|
|
52
|
--
class Foliage(Streamable):
|
|
53
|
--
# The entire foliage block, containing signature and the unsigned back pointer
|
|
54
|
--
# The hash of this is the "header hash". Note that for unfinished blocks, the prev_block_hash
|
|
55
|
--
# Is the prev from the signage point, and can be replaced with a more recent block
|
|
56
|
--
prev_block_hash: bytes32
|
|
57
|
--
reward_block_hash: bytes32
|
|
58
|
--
foliage_block_data: FoliageBlockData
|
|
59
|
--
foliage_block_data_signature: G2Element
|
|
60
|
--
foliage_transaction_block_hash: Optional[bytes32]
|
|
61
|
--
foliage_transaction_block_signature: Optional[G2Element]
|
|
5
|
++
TransactionsInfo = chia_rs.TransactionsInfo
|
|
6
|
++
FoliageTransactionBlock = chia_rs.FoliageTransactionBlock
|
|
7
|
++
FoliageBlockData = chia_rs.FoliageBlockData
|
|
8
|
++
Foliage = chia_rs.Foliage
|
|
@@@ -1,14 -1,14 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
|
|
3
|
++
import chia_rs
|
|
4
4
|
|
|
5
|
--
|
|
6
|
--
from chia.util.ints import uint32
|
|
7
|
--
from chia.util.streamable import Streamable, streamable
|
|
8
|
--
|
|
9
|
--
|
|
10
|
--
@streamable
|
|
11
|
--
@dataclass(frozen=True)
|
|
12
|
--
class PoolTarget(Streamable):
|
|
13
|
--
puzzle_hash: bytes32
|
|
14
|
--
max_height: uint32 # A max height of 0 means it is valid forever
|
|
5
|
++
PoolTarget = chia_rs.PoolTarget
|
|
@@@ -1,9 -1,9 +1,9 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
--
from dataclasses import dataclass
|
|
5
4
|
from typing import Optional, cast
|
|
6
5
|
|
|
6
|
++
import chia_rs
|
|
7
7
|
from bitstring import BitArray
|
|
8
8
|
from chia_rs import AugSchemeMPL, G1Element, PrivateKey
|
|
9
9
|
from chiapos import Verifier
|
|
@@@ -11,21 -11,21 +11,11 @@@
|
|
|
11
11
|
from chia.consensus.constants import ConsensusConstants
|
|
12
12
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
13
13
|
from chia.util.hash import std_hash
|
|
14
|
--
from chia.util.ints import
|
|
15
|
--
from chia.util.streamable import Streamable, streamable
|
|
16
|
--
|
|
17
|
--
log = logging.getLogger(__name__)
|
|
14
|
++
from chia.util.ints import uint32
|
|
18
15
|
|
|
16
|
++
ProofOfSpace = chia_rs.ProofOfSpace
|
|
19
17
|
|
|
20
|
--
|
|
21
|
--
@dataclass(frozen=True)
|
|
22
|
--
class ProofOfSpace(Streamable):
|
|
23
|
--
challenge: bytes32
|
|
24
|
--
pool_public_key: Optional[G1Element] # Only one of these two should be present
|
|
25
|
--
pool_contract_puzzle_hash: Optional[bytes32]
|
|
26
|
--
plot_public_key: G1Element
|
|
27
|
--
size: uint8
|
|
28
|
--
proof: bytes
|
|
18
|
++
log = logging.getLogger(__name__)
|
|
29
19
|
|
|
30
20
|
|
|
31
21
|
def get_plot_id(pos: ProofOfSpace) -> bytes32:
|
|
@@@ -1,56 -1,56 +1,6 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
|
|
4
|
--
from typing import Optional
|
|
3
|
++
import chia_rs
|
|
5
4
|
|
|
6
|
--
|
|
7
|
--
|
|
8
|
--
from chia.types.blockchain_format.proof_of_space import ProofOfSpace
|
|
9
|
--
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
10
|
--
from chia.types.blockchain_format.vdf import VDFInfo
|
|
11
|
--
from chia.util.ints import uint8, uint32, uint128
|
|
12
|
--
from chia.util.streamable import Streamable, streamable
|
|
13
|
--
|
|
14
|
--
|
|
15
|
--
@streamable
|
|
16
|
--
@dataclass(frozen=True)
|
|
17
|
--
class RewardChainBlockUnfinished(Streamable):
|
|
18
|
--
total_iters: uint128
|
|
19
|
--
signage_point_index: uint8
|
|
20
|
--
pos_ss_cc_challenge_hash: bytes32
|
|
21
|
--
proof_of_space: ProofOfSpace
|
|
22
|
--
challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
|
|
23
|
--
challenge_chain_sp_signature: G2Element
|
|
24
|
--
reward_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
|
|
25
|
--
reward_chain_sp_signature: G2Element
|
|
26
|
--
|
|
27
|
--
|
|
28
|
--
@streamable
|
|
29
|
--
@dataclass(frozen=True)
|
|
30
|
--
class RewardChainBlock(Streamable):
|
|
31
|
--
weight: uint128
|
|
32
|
--
height: uint32
|
|
33
|
--
total_iters: uint128
|
|
34
|
--
signage_point_index: uint8
|
|
35
|
--
pos_ss_cc_challenge_hash: bytes32
|
|
36
|
--
proof_of_space: ProofOfSpace
|
|
37
|
--
challenge_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
|
|
38
|
--
challenge_chain_sp_signature: G2Element
|
|
39
|
--
challenge_chain_ip_vdf: VDFInfo
|
|
40
|
--
reward_chain_sp_vdf: Optional[VDFInfo] # Not present for first sp in slot
|
|
41
|
--
reward_chain_sp_signature: G2Element
|
|
42
|
--
reward_chain_ip_vdf: VDFInfo
|
|
43
|
--
infused_challenge_chain_ip_vdf: Optional[VDFInfo] # Iff deficit < 16
|
|
44
|
--
is_transaction_block: bool
|
|
45
|
--
|
|
46
|
--
def get_unfinished(self) -> RewardChainBlockUnfinished:
|
|
47
|
--
return RewardChainBlockUnfinished(
|
|
48
|
--
self.total_iters,
|
|
49
|
--
self.signage_point_index,
|
|
50
|
--
self.pos_ss_cc_challenge_hash,
|
|
51
|
--
self.proof_of_space,
|
|
52
|
--
self.challenge_chain_sp_vdf,
|
|
53
|
--
self.challenge_chain_sp_signature,
|
|
54
|
--
self.reward_chain_sp_vdf,
|
|
55
|
--
self.reward_chain_sp_signature,
|
|
56
|
--
)
|
|
5
|
++
RewardChainBlock = chia_rs.RewardChainBlock
|
|
6
|
++
RewardChainBlockUnfinished = chia_rs.RewardChainBlockUnfinished
|
|
@@@ -1,115 -1,115 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
import
|
|
4
|
--
from typing import Tuple
|
|
3
|
++
import chia_rs
|
|
5
4
|
|
|
6
|
--
|
|
7
|
--
from clvm import SExp
|
|
8
|
--
from clvm.SExp import CastableType
|
|
9
|
--
|
|
10
|
--
from chia.types.blockchain_format.program import Program
|
|
11
|
--
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
12
|
--
from chia.util.byte_types import hexstr_to_bytes
|
|
13
|
--
|
|
14
|
--
|
|
15
|
--
def _serialize(node: object) -> bytes:
|
|
16
|
--
if isinstance(node, list):
|
|
17
|
--
serialized_list = bytearray()
|
|
18
|
--
for a in node:
|
|
19
|
--
serialized_list += b"\xff"
|
|
20
|
--
serialized_list += _serialize(a)
|
|
21
|
--
serialized_list += b"\x80"
|
|
22
|
--
return bytes(serialized_list)
|
|
23
|
--
if type(node) is SerializedProgram:
|
|
24
|
--
return bytes(node)
|
|
25
|
--
if type(node) is Program:
|
|
26
|
--
return bytes(node)
|
|
27
|
--
else:
|
|
28
|
--
ret: bytes = SExp.to(node).as_bin()
|
|
29
|
--
return ret
|
|
30
|
--
|
|
31
|
--
|
|
32
|
--
class SerializedProgram:
|
|
33
|
--
"""
|
|
34
|
--
An opaque representation of a clvm program. It has a more limited interface than a full SExp
|
|
35
|
--
"""
|
|
36
|
--
|
|
37
|
--
_buf: bytes
|
|
38
|
--
|
|
39
|
--
def __init__(self, buf: bytes) -> None:
|
|
40
|
--
assert isinstance(buf, bytes)
|
|
41
|
--
self._buf = buf
|
|
42
|
--
|
|
43
|
--
@staticmethod
|
|
44
|
--
def parse(f: io.BytesIO) -> SerializedProgram:
|
|
45
|
--
length = serialized_length(f.getvalue()[f.tell() :])
|
|
46
|
--
return SerializedProgram.from_bytes(f.read(length))
|
|
47
|
--
|
|
48
|
--
def stream(self, f: io.BytesIO) -> None:
|
|
49
|
--
f.write(self._buf)
|
|
50
|
--
|
|
51
|
--
@staticmethod
|
|
52
|
--
def from_bytes(blob: bytes) -> SerializedProgram:
|
|
53
|
--
assert serialized_length(blob) == len(blob)
|
|
54
|
--
return SerializedProgram(bytes(blob))
|
|
55
|
--
|
|
56
|
--
@staticmethod
|
|
57
|
--
def fromhex(hexstr: str) -> SerializedProgram:
|
|
58
|
--
return SerializedProgram.from_bytes(hexstr_to_bytes(hexstr))
|
|
59
|
--
|
|
60
|
--
@staticmethod
|
|
61
|
--
def from_program(p: Program) -> SerializedProgram:
|
|
62
|
--
return SerializedProgram(bytes(p))
|
|
63
|
--
|
|
64
|
--
@staticmethod
|
|
65
|
--
def to(o: CastableType) -> SerializedProgram:
|
|
66
|
--
return SerializedProgram(Program.to(o).as_bin())
|
|
67
|
--
|
|
68
|
--
def to_program(self) -> Program:
|
|
69
|
--
return Program.from_bytes(self._buf)
|
|
70
|
--
|
|
71
|
--
def uncurry(self) -> Tuple[Program, Program]:
|
|
72
|
--
return self.to_program().uncurry()
|
|
73
|
--
|
|
74
|
--
def __bytes__(self) -> bytes:
|
|
75
|
--
return self._buf
|
|
76
|
--
|
|
77
|
--
def __str__(self) -> str:
|
|
78
|
--
return bytes(self).hex()
|
|
79
|
--
|
|
80
|
--
def __repr__(self) -> str:
|
|
81
|
--
return f"{self.__class__.__name__}({str(self)})"
|
|
82
|
--
|
|
83
|
--
def __eq__(self, other: object) -> bool:
|
|
84
|
--
if not isinstance(other, SerializedProgram):
|
|
85
|
--
return False
|
|
86
|
--
return self._buf == other._buf
|
|
87
|
--
|
|
88
|
--
def __ne__(self, other: object) -> bool:
|
|
89
|
--
if not isinstance(other, SerializedProgram):
|
|
90
|
--
return True
|
|
91
|
--
return self._buf != other._buf
|
|
92
|
--
|
|
93
|
--
def get_tree_hash(self) -> bytes32:
|
|
94
|
--
return bytes32(tree_hash(self._buf))
|
|
95
|
--
|
|
96
|
--
def run_mempool_with_cost(self, max_cost: int, arg: object) -> Tuple[int, Program]:
|
|
97
|
--
return self._run(max_cost, MEMPOOL_MODE, arg)
|
|
98
|
--
|
|
99
|
--
def run_with_cost(self, max_cost: int, arg: object) -> Tuple[int, Program]:
|
|
100
|
--
return self._run(max_cost, 0, arg)
|
|
101
|
--
|
|
102
|
--
def _run(self, max_cost: int, flags: int, arg: object) -> Tuple[int, Program]:
|
|
103
|
--
# when multiple arguments are passed, concatenate them into a serialized
|
|
104
|
--
# buffer. Some arguments may already be in serialized form (e.g.
|
|
105
|
--
# SerializedProgram) so we don't want to de-serialize those just to
|
|
106
|
--
# serialize them back again. This is handled by _serialize()
|
|
107
|
--
serialized_args = _serialize(arg)
|
|
108
|
--
|
|
109
|
--
cost, ret = run_chia_program(
|
|
110
|
--
self._buf,
|
|
111
|
--
bytes(serialized_args),
|
|
112
|
--
max_cost,
|
|
113
|
--
flags,
|
|
114
|
--
)
|
|
115
|
--
return cost, Program.to(ret)
|
|
5
|
++
SerializedProgram = chia_rs.Program
|
|
@@@ -1,54 -1,54 +1,9 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
|
|
4
|
--
from typing import Optional
|
|
3
|
++
import chia_rs
|
|
5
4
|
|
|
6
|
--
|
|
7
|
--
|
|
8
|
--
|
|
9
|
--
|
|
10
|
--
|
|
11
|
--
from chia.util.ints import uint8, uint64
|
|
12
|
--
from chia.util.streamable import Streamable, streamable
|
|
13
|
--
|
|
14
|
--
|
|
15
|
--
@streamable
|
|
16
|
--
@dataclass(frozen=True)
|
|
17
|
--
class ChallengeBlockInfo(Streamable): # The hash of this is used as the challenge_hash for the ICC VDF
|
|
18
|
--
proof_of_space: ProofOfSpace
|
|
19
|
--
challenge_chain_sp_vdf: Optional[VDFInfo] # Only present if not the first sp
|
|
20
|
--
challenge_chain_sp_signature: G2Element
|
|
21
|
--
challenge_chain_ip_vdf: VDFInfo
|
|
22
|
--
|
|
23
|
--
|
|
24
|
--
@streamable
|
|
25
|
--
@dataclass(frozen=True)
|
|
26
|
--
class ChallengeChainSubSlot(Streamable):
|
|
27
|
--
challenge_chain_end_of_slot_vdf: VDFInfo
|
|
28
|
--
infused_challenge_chain_sub_slot_hash: Optional[bytes32] # Only at the end of a slot
|
|
29
|
--
subepoch_summary_hash: Optional[bytes32] # Only once per sub-epoch, and one sub-epoch delayed
|
|
30
|
--
new_sub_slot_iters: Optional[uint64] # Only at the end of epoch, sub-epoch, and slot
|
|
31
|
--
new_difficulty: Optional[uint64] # Only at the end of epoch, sub-epoch, and slot
|
|
32
|
--
|
|
33
|
--
|
|
34
|
--
@streamable
|
|
35
|
--
@dataclass(frozen=True)
|
|
36
|
--
class InfusedChallengeChainSubSlot(Streamable):
|
|
37
|
--
infused_challenge_chain_end_of_slot_vdf: VDFInfo
|
|
38
|
--
|
|
39
|
--
|
|
40
|
--
@streamable
|
|
41
|
--
@dataclass(frozen=True)
|
|
42
|
--
class RewardChainSubSlot(Streamable):
|
|
43
|
--
end_of_slot_vdf: VDFInfo
|
|
44
|
--
challenge_chain_sub_slot_hash: bytes32
|
|
45
|
--
infused_challenge_chain_sub_slot_hash: Optional[bytes32]
|
|
46
|
--
deficit: uint8 # 16 or less. usually zero
|
|
47
|
--
|
|
48
|
--
|
|
49
|
--
@streamable
|
|
50
|
--
@dataclass(frozen=True)
|
|
51
|
--
class SubSlotProofs(Streamable):
|
|
52
|
--
challenge_chain_slot_proof: VDFProof
|
|
53
|
--
infused_challenge_chain_slot_proof: Optional[VDFProof]
|
|
54
|
--
reward_chain_slot_proof: VDFProof
|
|
5
|
++
ChallengeBlockInfo = chia_rs.ChallengeBlockInfo
|
|
6
|
++
ChallengeChainSubSlot = chia_rs.ChallengeChainSubSlot
|
|
7
|
++
InfusedChallengeChainSubSlot = chia_rs.InfusedChallengeChainSubSlot
|
|
8
|
++
RewardChainSubSlot = chia_rs.RewardChainSubSlot
|
|
9
|
++
SubSlotProofs = chia_rs.SubSlotProofs
|
|
@@@ -1,18 -1,18 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
|
|
4
|
--
from typing import Optional
|
|
3
|
++
import chia_rs
|
|
5
4
|
|
|
6
|
--
|
|
7
|
--
from chia.util.ints import uint8, uint64
|
|
8
|
--
from chia.util.streamable import Streamable, streamable
|
|
9
|
--
|
|
10
|
--
|
|
11
|
--
@streamable
|
|
12
|
--
@dataclass(frozen=True)
|
|
13
|
--
class SubEpochSummary(Streamable):
|
|
14
|
--
prev_subepoch_summary_hash: bytes32
|
|
15
|
--
reward_chain_hash: bytes32 # hash of reward chain at end of last segment
|
|
16
|
--
num_blocks_overflow: uint8 # How many more blocks than 384*(N-1)
|
|
17
|
--
new_difficulty: Optional[uint64] # Only once per epoch (diff adjustment)
|
|
18
|
--
new_sub_slot_iters: Optional[uint64] # Only once per epoch (diff adjustment)
|
|
5
|
++
SubEpochSummary = chia_rs.SubEpochSummary
|
|
@@@ -2,21 -2,21 +2,22 @@@ from __future__ import annotation
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import traceback
|
|
5
|
--
from dataclasses import dataclass
|
|
6
5
|
from enum import IntEnum
|
|
7
6
|
from functools import lru_cache
|
|
8
7
|
from typing import Optional
|
|
9
8
|
|
|
9
|
++
from chia_rs import VDFInfo, VDFProof
|
|
10
10
|
from chiavdf import create_discriminant, verify_n_wesolowski
|
|
11
11
|
|
|
12
12
|
from chia.consensus.constants import ConsensusConstants
|
|
13
13
|
from chia.types.blockchain_format.classgroup import ClassgroupElement
|
|
14
14
|
from chia.types.blockchain_format.sized_bytes import bytes32, bytes100
|
|
15
15
|
from chia.util.ints import uint8, uint64
|
|
16
|
--
from chia.util.streamable import Streamable, streamable
|
|
17
16
|
|
|
18
17
|
log = logging.getLogger(__name__)
|
|
19
18
|
|
|
19
|
++
__all__ = ["VDFInfo", "VDFProof"]
|
|
20
|
++
|
|
20
21
|
|
|
21
22
|
@lru_cache(maxsize=200)
|
|
22
23
|
def get_discriminant(challenge: bytes32, size_bites: int) -> int:
|
|
@@@ -46,22 -46,22 +47,6 @@@ def verify_vdf
|
|
|
46
47
|
)
|
|
47
48
|
|
|
48
49
|
|
|
49
|
--
@streamable
|
|
50
|
--
@dataclass(frozen=True)
|
|
51
|
--
class VDFInfo(Streamable):
|
|
52
|
--
challenge: bytes32 # Used to generate the discriminant (VDF group)
|
|
53
|
--
number_of_iterations: uint64
|
|
54
|
--
output: ClassgroupElement
|
|
55
|
--
|
|
56
|
--
|
|
57
|
--
@streamable
|
|
58
|
--
@dataclass(frozen=True)
|
|
59
|
--
class VDFProof(Streamable):
|
|
60
|
--
witness_type: uint8
|
|
61
|
--
witness: bytes
|
|
62
|
--
normalized_to_identity: bool
|
|
63
|
--
|
|
64
|
--
|
|
65
50
|
def validate_vdf(
|
|
66
51
|
proof: VDFProof,
|
|
67
52
|
constants: ConsensusConstants,
|
|
@@@ -1,21 -1,21 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
|
|
4
|
--
from typing import Optional
|
|
3
|
++
import chia_rs
|
|
5
4
|
|
|
6
|
--
|
|
7
|
--
ChallengeChainSubSlot,
|
|
8
|
--
InfusedChallengeChainSubSlot,
|
|
9
|
--
RewardChainSubSlot,
|
|
10
|
--
SubSlotProofs,
|
|
11
|
--
)
|
|
12
|
--
from chia.util.streamable import Streamable, streamable
|
|
13
|
--
|
|
14
|
--
|
|
15
|
--
@streamable
|
|
16
|
--
@dataclass(frozen=True)
|
|
17
|
--
class EndOfSubSlotBundle(Streamable):
|
|
18
|
--
challenge_chain: ChallengeChainSubSlot
|
|
19
|
--
infused_challenge_chain: Optional[InfusedChallengeChainSubSlot]
|
|
20
|
--
reward_chain: RewardChainSubSlot
|
|
21
|
--
proofs: SubSlotProofs
|
|
5
|
++
EndOfSubSlotBundle = chia_rs.EndOfSubSlotBundle
|
|
@@@ -39,15 -39,15 +39,15 @@@ class FullBlock(Streamable)
|
|
|
39
39
|
|
|
40
40
|
@property
|
|
41
41
|
def height(self) -> uint32:
|
|
42
|
--
return self.reward_chain_block.height
|
|
42
|
++
return uint32(self.reward_chain_block.height)
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
45
|
def weight(self) -> uint128:
|
|
46
|
--
return self.reward_chain_block.weight
|
|
46
|
++
return uint128(self.reward_chain_block.weight)
|
|
47
47
|
|
|
48
48
|
@property
|
|
49
49
|
def total_iters(self) -> uint128:
|
|
50
|
--
return self.reward_chain_block.total_iters
|
|
50
|
++
return uint128(self.reward_chain_block.total_iters)
|
|
51
51
|
|
|
52
52
|
@property
|
|
53
53
|
def header_hash(self) -> bytes32:
|
|
@@@ -38,11 -38,11 +38,11 @@@ class HeaderBlock(Streamable)
|
|
|
38
38
|
|
|
39
39
|
@property
|
|
40
40
|
def height(self) -> uint32:
|
|
41
|
--
return self.reward_chain_block.height
|
|
41
|
++
return uint32(self.reward_chain_block.height)
|
|
42
42
|
|
|
43
43
|
@property
|
|
44
44
|
def weight(self) -> uint128:
|
|
45
|
--
return self.reward_chain_block.weight
|
|
45
|
++
return uint128(self.reward_chain_block.weight)
|
|
46
46
|
|
|
47
47
|
@property
|
|
48
48
|
def header_hash(self) -> bytes32:
|
|
@@@ -50,7 -50,7 +50,7 @@@
|
|
|
50
50
|
|
|
51
51
|
@property
|
|
52
52
|
def total_iters(self) -> uint128:
|
|
53
|
--
return self.reward_chain_block.total_iters
|
|
53
|
++
return uint128(self.reward_chain_block.total_iters)
|
|
54
54
|
|
|
55
55
|
@property
|
|
56
56
|
def log_string(self) -> str:
|
|
@@@ -1,6 -1,6 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
import asyncio
|
|
4
3
|
from dataclasses import dataclass, field
|
|
5
4
|
from typing import Optional, Tuple
|
|
6
5
|
|
|
@@@ -9,6 -9,6 +8,7 @@@ from chia.types.blockchain_format.sized
|
|
|
9
8
|
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
|
10
9
|
from chia.types.spend_bundle import SpendBundle
|
|
11
10
|
from chia.util.errors import Err
|
|
11
|
++
from chia.util.misc import ValuedEvent
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@dataclass(frozen=True)
|
|
@@@ -22,7 -22,7 +22,7 @@@ class TransactionQueueEntry
|
|
|
22
22
|
spend_name: bytes32
|
|
23
23
|
peer: Optional[WSChiaConnection] = field(compare=False)
|
|
24
24
|
test: bool = field(compare=False)
|
|
25
|
--
done:
|
|
26
|
--
default_factory=
|
|
25
|
++
done: ValuedEvent[Tuple[MempoolInclusionStatus, Optional[Err]]] = field(
|
|
26
|
++
default_factory=ValuedEvent,
|
|
27
27
|
compare=False,
|
|
28
28
|
)
|
|
@@@ -42,4 -42,4 +42,4 @@@ class UnfinishedBlock(Streamable)
|
|
|
42
42
|
|
|
43
43
|
@property
|
|
44
44
|
def total_iters(self) -> uint128:
|
|
45
|
--
return self.reward_chain_block.total_iters
|
|
45
|
++
return uint128(self.reward_chain_block.total_iters)
|
|
@@@ -34,4 -34,4 +34,4 @@@ class UnfinishedHeaderBlock(Streamable)
|
|
|
34
34
|
|
|
35
35
|
@property
|
|
36
36
|
def total_iters(self) -> uint128:
|
|
37
|
--
return self.reward_chain_block.total_iters
|
|
37
|
++
return uint128(self.reward_chain_block.total_iters)
|
|
@@@ -1,26 -1,26 +1,16 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
--
from typing import List
|
|
4
|
++
from typing import List
|
|
5
|
++
|
|
6
|
++
import chia_rs
|
|
5
7
|
|
|
6
|
--
from chia.types.blockchain_format.proof_of_space import ProofOfSpace
|
|
7
8
|
from chia.types.blockchain_format.reward_chain_block import RewardChainBlock
|
|
8
|
--
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
9
|
--
from chia.types.blockchain_format.vdf import VDFInfo, VDFProof
|
|
10
9
|
from chia.types.end_of_slot_bundle import EndOfSubSlotBundle
|
|
11
10
|
from chia.types.header_block import HeaderBlock
|
|
12
|
--
from chia.util.ints import uint8, uint32, uint64, uint128
|
|
13
11
|
from chia.util.streamable import Streamable, streamable
|
|
14
12
|
|
|
15
|
--
|
|
16
|
--
@streamable
|
|
17
|
--
@dataclass(frozen=True)
|
|
18
|
--
class SubEpochData(Streamable):
|
|
19
|
--
reward_chain_hash: bytes32
|
|
20
|
--
num_blocks_overflow: uint8
|
|
21
|
--
new_sub_slot_iters: Optional[uint64]
|
|
22
|
--
new_difficulty: Optional[uint64]
|
|
23
|
--
|
|
13
|
++
SubEpochData = chia_rs.SubEpochData
|
|
24
14
|
|
|
25
15
|
# number of challenge blocks
|
|
26
16
|
# Average iters for challenge blocks
|
|
@@@ -33,53 -33,53 +23,9 @@@
|
|
|
33
23
|
# total number of challenge blocks == total number of reward chain blocks
|
|
34
24
|
|
|
35
25
|
|
|
36
|
--
|
|
37
|
--
|
|
38
|
--
|
|
39
|
--
# if infused
|
|
40
|
--
proof_of_space: Optional[ProofOfSpace]
|
|
41
|
--
# VDF to signage point
|
|
42
|
--
cc_signage_point: Optional[VDFProof]
|
|
43
|
--
# VDF from signage to infusion point
|
|
44
|
--
cc_infusion_point: Optional[VDFProof]
|
|
45
|
--
icc_infusion_point: Optional[VDFProof]
|
|
46
|
--
cc_sp_vdf_info: Optional[VDFInfo]
|
|
47
|
--
signage_point_index: Optional[uint8]
|
|
48
|
--
# VDF from beginning to end of slot if not infused
|
|
49
|
--
# from ip to end if infused
|
|
50
|
--
cc_slot_end: Optional[VDFProof]
|
|
51
|
--
icc_slot_end: Optional[VDFProof]
|
|
52
|
--
# info from finished slots
|
|
53
|
--
cc_slot_end_info: Optional[VDFInfo]
|
|
54
|
--
icc_slot_end_info: Optional[VDFInfo]
|
|
55
|
--
cc_ip_vdf_info: Optional[VDFInfo]
|
|
56
|
--
icc_ip_vdf_info: Optional[VDFInfo]
|
|
57
|
--
total_iters: Optional[uint128]
|
|
58
|
--
|
|
59
|
--
def is_challenge(self) -> bool:
|
|
60
|
--
if self.proof_of_space is not None:
|
|
61
|
--
return True
|
|
62
|
--
return False
|
|
63
|
--
|
|
64
|
--
def is_end_of_slot(self) -> bool:
|
|
65
|
--
if self.cc_slot_end_info is not None:
|
|
66
|
--
return True
|
|
67
|
--
return False
|
|
68
|
--
|
|
69
|
--
|
|
70
|
--
@streamable
|
|
71
|
--
@dataclass(frozen=True)
|
|
72
|
--
class SubEpochChallengeSegment(Streamable):
|
|
73
|
--
sub_epoch_n: uint32
|
|
74
|
--
sub_slots: List[SubSlotData]
|
|
75
|
--
rc_slot_end_info: Optional[VDFInfo] # in first segment of each sub_epoch
|
|
76
|
--
|
|
77
|
--
|
|
78
|
--
@streamable
|
|
79
|
--
@dataclass(frozen=True)
|
|
80
|
--
# this is used only for serialization to database
|
|
81
|
--
class SubEpochSegments(Streamable):
|
|
82
|
--
challenge_segments: List[SubEpochChallengeSegment]
|
|
26
|
++
SubEpochChallengeSegment = chia_rs.SubEpochChallengeSegment
|
|
27
|
++
SubEpochSegments = chia_rs.SubEpochSegments
|
|
28
|
++
SubSlotData = chia_rs.SubSlotData
|
|
83
29
|
|
|
84
30
|
|
|
85
31
|
@streamable
|
|
@@@ -1,6 -1,6 +1,5 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
import io
|
|
4
3
|
from dataclasses import dataclass
|
|
5
4
|
from typing import Callable, List, Optional, Tuple
|
|
6
5
|
|
|
@@@ -298,8 -298,8 +297,7 @@@ def header_block_from_block
|
|
|
298
297
|
transactions_info_optional = bytes([0])
|
|
299
298
|
else:
|
|
300
299
|
transactions_info_optional = bytes([1])
|
|
301
|
--
|
|
302
|
--
transactions_info = TransactionsInfo.parse(io.BytesIO(buf3))
|
|
300
|
++
transactions_info, advance = TransactionsInfo.parse_rust(buf2[1:])
|
|
303
301
|
byte_array_tx: List[bytearray] = []
|
|
304
302
|
if is_transaction_block and transactions_info:
|
|
305
303
|
addition_coins = tx_addition_coins + list(transactions_info.reward_claims_incorporated)
|
|
@@@ -440,6 -440,6 +440,11 @@@ full_node
|
|
|
440
440
|
# analyze with chia/utils/profiler.py
|
|
441
441
|
enable_profiler: False
|
|
442
442
|
|
|
443
|
++
# when enabled, each time a block is validated, the python profiler is
|
|
444
|
++
# engaged. If the validation takes more than 2 seconds, the profile is saved
|
|
445
|
++
# to disk, in the chia root/block-validation-profile
|
|
446
|
++
profile_block_validation: False
|
|
447
|
++
|
|
443
448
|
enable_memory_profiler: False
|
|
444
449
|
|
|
445
450
|
# this is a debug and profiling facility that logs all SQLite commands to a
|
|
@@@ -2,7 -2,7 +2,7 @@@ from __future__ import annotation
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from sys import platform
|
|
5
|
--
from typing import Optional, Tuple, Union, overload
|
|
5
|
++
from typing import ClassVar, Optional, Tuple, Union, overload
|
|
6
6
|
|
|
7
7
|
from keyring.backends.macOS import Keyring as MacKeyring
|
|
8
8
|
from keyring.backends.Windows import WinVaultKeyring as WinKeyring
|
|
@@@ -63,8 -63,8 +63,8 @@@ class KeyringWrapper
|
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
65
|
# Static members
|
|
66
|
--
__shared_instance = None
|
|
67
|
--
__keys_root_path: Path = DEFAULT_KEYS_ROOT_PATH
|
|
66
|
++
__shared_instance: ClassVar[Optional[KeyringWrapper]] = None
|
|
67
|
++
__keys_root_path: ClassVar[Path] = DEFAULT_KEYS_ROOT_PATH
|
|
68
68
|
|
|
69
69
|
# Instance members
|
|
70
70
|
keys_root_path: Path
|
|
@@@ -13,6 -13,6 +13,7 @@@ from typing import
|
|
|
13
13
|
Any,
|
|
14
14
|
AsyncContextManager,
|
|
15
15
|
AsyncIterator,
|
|
16
|
++
ClassVar,
|
|
16
17
|
Collection,
|
|
17
18
|
ContextManager,
|
|
18
19
|
Dict,
|
|
@@@ -374,3 -374,3 +375,27 @@@ async def split_async_manager(manager:
|
|
|
374
375
|
yield split
|
|
375
376
|
finally:
|
|
376
377
|
await split.exit(if_needed=True)
|
|
378
|
++
|
|
379
|
++
|
|
380
|
++
class ValuedEventSentinel:
|
|
381
|
++
pass
|
|
382
|
++
|
|
383
|
++
|
|
384
|
++
@dataclasses.dataclass
|
|
385
|
++
class ValuedEvent(Generic[T]):
|
|
386
|
++
_value_sentinel: ClassVar[ValuedEventSentinel] = ValuedEventSentinel()
|
|
387
|
++
|
|
388
|
++
_event: asyncio.Event = dataclasses.field(default_factory=asyncio.Event)
|
|
389
|
++
_value: Union[ValuedEventSentinel, T] = _value_sentinel
|
|
390
|
++
|
|
391
|
++
def set(self, value: T) -> None:
|
|
392
|
++
if not isinstance(self._value, ValuedEventSentinel):
|
|
393
|
++
raise Exception("Value already set")
|
|
394
|
++
self._value = value
|
|
395
|
++
self._event.set()
|
|
396
|
++
|
|
397
|
++
async def wait(self) -> T:
|
|
398
|
++
await self._event.wait()
|
|
399
|
++
if isinstance(self._value, ValuedEventSentinel):
|
|
400
|
++
raise Exception("Value not set despite event being set")
|
|
401
|
++
return self._value
|
|
@@@ -5,7 -5,7 +5,9 @@@ import cProfil
|
|
|
5
5
|
import logging
|
|
6
6
|
import pathlib
|
|
7
7
|
import tracemalloc
|
|
8
|
++
from contextlib import asynccontextmanager
|
|
8
9
|
from datetime import datetime
|
|
10
|
++
from typing import AsyncIterator, Optional
|
|
9
11
|
|
|
10
12
|
from chia.util.path import path_from_root
|
|
11
13
|
|
|
@@@ -176,3 -176,3 +178,17 @@@ async def mem_profile_task(root_path: p
|
|
|
176
178
|
counter += 1
|
|
177
179
|
finally:
|
|
178
180
|
tracemalloc.stop()
|
|
181
|
++
|
|
182
|
++
|
|
183
|
++
@asynccontextmanager
|
|
184
|
++
async def enable_profiler(profile: bool) -> AsyncIterator[Optional[cProfile.Profile]]:
|
|
185
|
++
if not profile:
|
|
186
|
++
yield None
|
|
187
|
++
return
|
|
188
|
++
|
|
189
|
++
# this is not covered by any unit tests as it's essentially test code
|
|
190
|
++
# itself. It's exercised manually when investigating performance issues
|
|
191
|
++
with cProfile.Profile() as pr: # pragma: no cover
|
|
192
|
++
pr.enable()
|
|
193
|
++
yield pr
|
|
194
|
++
pr.disable()
|
|
@@@ -7,6 -7,6 +7,16 @@@ from typing import An
|
|
|
7
7
|
def recursive_replace(root_obj: Any, replace_str: str, replace_with: Any) -> Any:
|
|
8
8
|
split_str = replace_str.split(".")
|
|
9
9
|
if len(split_str) == 1:
|
|
10
|
--
|
|
10
|
++
# This check is here to support native types (implemented in Rust
|
|
11
|
++
# in chia_rs) that aren't dataclasses. They instead implement a
|
|
12
|
++
# replace() method in their python bindings.
|
|
13
|
++
if hasattr(root_obj, "replace"):
|
|
14
|
++
return root_obj.replace(**{split_str[0]: replace_with})
|
|
15
|
++
else:
|
|
16
|
++
return replace(root_obj, **{split_str[0]: replace_with})
|
|
11
17
|
sub_obj = recursive_replace(getattr(root_obj, split_str[0]), ".".join(split_str[1:]), replace_with)
|
|
12
|
--
|
|
18
|
++
# See comment above
|
|
19
|
++
if hasattr(root_obj, "replace"):
|
|
20
|
++
return root_obj.replace(**{split_str[0]: sub_obj})
|
|
21
|
++
else:
|
|
22
|
++
return replace(root_obj, **{split_str[0]: sub_obj})
|
|
@@@ -1,6 -1,6 +1,6 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
from typing import BinaryIO, ClassVar, SupportsIndex, SupportsInt, Type, TypeVar, Union
|
|
3
|
++
from typing import BinaryIO, ClassVar, Optional, SupportsIndex, SupportsInt, Type, TypeVar, Union
|
|
4
4
|
|
|
5
5
|
from typing_extensions import Protocol
|
|
6
6
|
|
|
@@@ -70,6 -70,6 +70,13 @@@ class StructStream(int)
|
|
|
70
70
|
if not (self.MINIMUM <= self <= self.MAXIMUM):
|
|
71
71
|
raise ValueError(f"Value {self} does not fit into {type(self).__name__}")
|
|
72
72
|
|
|
73
|
++
@classmethod
|
|
74
|
++
def construct_optional(cls: Type[_T_StructStream], val: Optional[int]) -> Optional[_T_StructStream]:
|
|
75
|
++
if val is None:
|
|
76
|
++
return None
|
|
77
|
++
else:
|
|
78
|
++
return cls(val)
|
|
79
|
++
|
|
73
80
|
@classmethod
|
|
74
81
|
def parse(cls: Type[_T_StructStream], f: BinaryIO) -> _T_StructStream:
|
|
75
82
|
read_bytes = f.read(cls.SIZE)
|
|
@@@ -1,37 -1,37 +1,0 @@@
|
|
|
1
|
--
from __future__ import annotations
|
|
2
|
--
|
|
3
|
--
from dataclasses import dataclass
|
|
4
|
--
from typing import List
|
|
5
|
--
|
|
6
|
--
from chia.types.blockchain_format.coin import Coin
|
|
7
|
--
from chia.types.header_block import HeaderBlock
|
|
8
|
--
from chia.util.streamable import Streamable, streamable
|
|
9
|
--
|
|
10
|
--
|
|
11
|
--
@streamable
|
|
12
|
--
@dataclass(frozen=True)
|
|
13
|
--
class HeaderBlockRecord(Streamable):
|
|
14
|
--
"""
|
|
15
|
--
These are values that are stored in the wallet database, corresponding to information
|
|
16
|
--
that the wallet cares about in each block
|
|
17
|
--
"""
|
|
18
|
--
|
|
19
|
--
header: HeaderBlock
|
|
20
|
--
additions: List[Coin] # A block record without additions is not finished
|
|
21
|
--
removals: List[Coin] # A block record without removals is not finished
|
|
22
|
--
|
|
23
|
--
@property
|
|
24
|
--
def header_hash(self):
|
|
25
|
--
return self.header.header_hash
|
|
26
|
--
|
|
27
|
--
@property
|
|
28
|
--
def prev_header_hash(self):
|
|
29
|
--
return self.header.prev_header_hash
|
|
30
|
--
|
|
31
|
--
@property
|
|
32
|
--
def height(self):
|
|
33
|
--
return self.header.height
|
|
34
|
--
|
|
35
|
--
@property
|
|
36
|
--
def transactions_filter(self):
|
|
37
|
--
return self.header.transactions_filter
|
|
@@@ -2,7 -2,7 +2,7 @@@ from __future__ import annotation
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from dataclasses import dataclass, fields, replace
|
|
5
|
--
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, final, get_type_hints
|
|
5
|
++
from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, cast, final, get_type_hints
|
|
6
6
|
|
|
7
7
|
from chia_rs import G1Element
|
|
8
8
|
from clvm.casts import int_from_bytes, int_to_bytes
|
|
@@@ -733,8 -733,8 +733,8 @@@ class UnknownCondition(Condition)
|
|
|
733
733
|
args: List[Program]
|
|
734
734
|
|
|
735
735
|
def to_program(self) -> Program:
|
|
736
|
--
|
|
737
|
--
return
|
|
736
|
++
# TODO: Remove cast when we have proper hinting for this
|
|
737
|
++
return cast(Program, self.opcode.cons(Program.to(self.args)))
|
|
738
738
|
|
|
739
739
|
@classmethod
|
|
740
740
|
def from_program(cls, program: Program) -> UnknownCondition:
|
|
@@@ -428,6 -428,6 +428,7 @@@ class DIDWallet
|
|
|
428
428
|
)
|
|
429
429
|
|
|
430
430
|
await self.add_parent(coin.name(), future_parent)
|
|
431
|
++
await self.wallet_state_manager.add_interested_coin_ids([coin.name()])
|
|
431
432
|
|
|
432
433
|
def create_backup(self) -> str:
|
|
433
434
|
"""
|
|
@@@ -902,6 -902,6 +903,9 @@@
|
|
|
902
903
|
) -> Tuple[TransactionRecord, SpendBundle, str]:
|
|
903
904
|
"""
|
|
904
905
|
Create an attestment
|
|
906
|
++
TODO:
|
|
907
|
++
1. We should use/respect `tx_config` (reuse_puzhash and co)
|
|
908
|
++
2. We should take a fee as it's a requirement for every transaction function to do so
|
|
905
909
|
:param recovering_coin_name: Coin ID of the DID
|
|
906
910
|
:param newpuz: New puzzle hash
|
|
907
911
|
:param pubkey: New wallet pubkey
|
|
@@@ -1116,27 -1116,27 +1120,20 @@@
|
|
|
1116
1120
|
async def get_new_p2_inner_puzzle(self) -> Program:
|
|
1117
1121
|
return await self.standard_wallet.get_new_puzzle()
|
|
1118
1122
|
|
|
1119
|
--
async def get_new_did_innerpuz(self, origin_id=None) -> Program:
|
|
1123
|
++
async def get_new_did_innerpuz(self, origin_id: Optional[bytes32] = None) -> Program:
|
|
1120
1124
|
if self.did_info.origin_coin is not None:
|
|
1121
|
--
|
|
1122
|
--
p2_puzzle_or_hash=await self.get_new_p2_inner_puzzle(),
|
|
1123
|
--
recovery_list=self.did_info.backup_ids,
|
|
1124
|
--
num_of_backup_ids_needed=uint64(self.did_info.num_of_backup_ids_needed),
|
|
1125
|
--
launcher_id=self.did_info.origin_coin.name(),
|
|
1126
|
--
metadata=did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
|
|
1127
|
--
)
|
|
1125
|
++
launcher_id = self.did_info.origin_coin.name()
|
|
1128
1126
|
elif origin_id is not None:
|
|
1129
|
--
|
|
1130
|
--
p2_puzzle_or_hash=await self.get_new_p2_inner_puzzle(),
|
|
1131
|
--
recovery_list=self.did_info.backup_ids,
|
|
1132
|
--
num_of_backup_ids_needed=uint64(self.did_info.num_of_backup_ids_needed),
|
|
1133
|
--
launcher_id=origin_id,
|
|
1134
|
--
metadata=did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
|
|
1135
|
--
)
|
|
1127
|
++
launcher_id = origin_id
|
|
1136
1128
|
else:
|
|
1137
1129
|
raise ValueError("must have origin coin")
|
|
1138
|
--
|
|
1139
|
--
|
|
1130
|
++
return did_wallet_puzzles.create_innerpuz(
|
|
1131
|
++
p2_puzzle_or_hash=await self.get_new_p2_inner_puzzle(),
|
|
1132
|
++
recovery_list=self.did_info.backup_ids,
|
|
1133
|
++
num_of_backup_ids_needed=self.did_info.num_of_backup_ids_needed,
|
|
1134
|
++
launcher_id=launcher_id,
|
|
1135
|
++
metadata=did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
|
|
1136
|
++
)
|
|
1140
1137
|
|
|
1141
1138
|
async def get_new_did_inner_hash(self) -> bytes32:
|
|
1142
1139
|
innerpuz = await self.get_new_did_innerpuz()
|
|
@@@ -1366,9 -1366,9 +1363,6 @@@
|
|
|
1366
1363
|
unsigned_spend_bundle = SpendBundle(list_of_coinspends, G2Element())
|
|
1367
1364
|
return await self.sign(unsigned_spend_bundle)
|
|
1368
1365
|
|
|
1369
|
--
async def get_frozen_amount(self) -> uint64:
|
|
1370
|
--
return await self.wallet_state_manager.get_frozen_balance(self.wallet_info.id)
|
|
1371
|
--
|
|
1372
1366
|
async def get_spendable_balance(self, unspent_records=None) -> uint128:
|
|
1373
1367
|
spendable_am = await self.wallet_state_manager.get_confirmed_spendable_balance_for_wallet(
|
|
1374
1368
|
self.wallet_info.id, unspent_records
|
|
@@@ -1509,7 -1509,7 +1503,7 @@@
|
|
|
1509
1503
|
)
|
|
1510
1504
|
if len(spendable_coins) == 0:
|
|
1511
1505
|
raise RuntimeError("DID is not currently spendable")
|
|
1512
|
--
return list(spendable_coins)[0].coin
|
|
1506
|
++
return sorted(list(spendable_coins), key=lambda c: c.confirmed_block_height, reverse=True)[0].coin
|
|
1513
1507
|
|
|
1514
1508
|
async def match_hinted_coin(self, coin: Coin, hint: bytes32) -> bool:
|
|
1515
1509
|
if self.did_info.origin_coin is None:
|
|
@@@ -1,6 -1,6 +1,6 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
--
from typing import Dict, Iterator, List, Optional, Tuple, Union
|
|
3
|
++
from typing import Dict, Iterator, List, Optional, Tuple, Union, cast
|
|
4
4
|
|
|
5
5
|
from chia_rs import G1Element
|
|
6
6
|
|
|
@@@ -48,7 -48,7 +48,7 @@@ def create_innerpuz
|
|
|
48
48
|
Note: Receiving a standard P2 puzzle hash wouldn't calculate a valid puzzle, but
|
|
49
49
|
that can be useful if calling `.get_tree_hash_precalc()` on it.
|
|
50
50
|
"""
|
|
51
|
--
backup_ids_hash = Program
|
|
51
|
++
backup_ids_hash = Program.to(recovery_list).get_tree_hash()
|
|
52
52
|
if recovery_list_hash is not None:
|
|
53
53
|
backup_ids_hash = recovery_list_hash
|
|
54
54
|
singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, SINGLETON_LAUNCHER_PUZZLE_HASH)))
|
|
@@@ -123,7 -123,7 +123,7 @@@ def create_recovery_message_puzzle(reco
|
|
|
123
123
|
:param pubkey: New wallet pubkey
|
|
124
124
|
:return: Message puzzle
|
|
125
125
|
"""
|
|
126
|
--
|
|
126
|
++
puzzle = Program.to(
|
|
127
127
|
(
|
|
128
128
|
1,
|
|
129
129
|
[
|
|
@@@ -132,6 -132,6 +132,8 @@@
|
|
|
132
132
|
],
|
|
133
133
|
)
|
|
134
134
|
)
|
|
135
|
++
# TODO: Remove cast when we have proper hinting for this
|
|
136
|
++
return cast(Program, puzzle)
|
|
135
137
|
|
|
136
138
|
|
|
137
139
|
def create_spend_for_message(
|
|
@@@ -153,7 -153,7 +155,7 @@@
|
|
|
153
155
|
|
|
154
156
|
def match_did_puzzle(mod: Program, curried_args: Program) -> Optional[Iterator[Program]]:
|
|
155
157
|
"""
|
|
156
|
--
|
|
158
|
++
Given a puzzle test if it's a DID, if it is, return the curried arguments
|
|
157
159
|
:param puzzle: Puzzle
|
|
158
160
|
:return: Curried parameters
|
|
159
161
|
"""
|
|
@@@ -161,7 -161,7 +163,8 @@@
|
|
|
161
163
|
if mod == SINGLETON_TOP_LAYER_MOD:
|
|
162
164
|
mod, curried_args = curried_args.rest().first().uncurry()
|
|
163
165
|
if mod == DID_INNERPUZ_MOD:
|
|
164
|
--
|
|
166
|
++
# TODO: Remove cast when we have clvm type hinting for this
|
|
167
|
++
return cast(Iterator[Program], curried_args.as_iter())
|
|
165
168
|
except Exception:
|
|
166
169
|
import traceback
|
|
167
170
|
|
|
@@@ -178,11 -178,11 +181,11 @@@ def check_is_did_puzzle(puzzle: Program
|
|
|
178
181
|
r = puzzle.uncurry()
|
|
179
182
|
if r is None:
|
|
180
183
|
return False
|
|
181
|
--
inner_f,
|
|
184
|
++
inner_f, _ = r
|
|
182
185
|
return is_singleton(inner_f)
|
|
183
186
|
|
|
184
187
|
|
|
185
|
--
def metadata_to_program(metadata: Dict) -> Program:
|
|
188
|
++
def metadata_to_program(metadata: Dict[str, str]) -> Program:
|
|
186
189
|
"""
|
|
187
190
|
Convert the metadata dict to a Chialisp program
|
|
188
191
|
:param metadata: User defined metadata
|
|
@@@ -191,10 -191,10 +194,11 @@@
|
|
|
191
194
|
kv_list = []
|
|
192
195
|
for key, value in metadata.items():
|
|
193
196
|
kv_list.append((key, value))
|
|
194
|
--
|
|
197
|
++
# TODO: Remove cast when we have proper hinting for this
|
|
198
|
++
return cast(Program, Program.to(kv_list))
|
|
195
199
|
|
|
196
200
|
|
|
197
|
--
def did_program_to_metadata(program: Program) -> Dict:
|
|
201
|
++
def did_program_to_metadata(program: Program) -> Dict[str, str]:
|
|
198
202
|
"""
|
|
199
203
|
Convert a program to a metadata dict
|
|
200
204
|
:param program: Chialisp program contains the metadata
|
|
@@@ -29,14 -29,14 +29,14 @@@ class KeyValStore
|
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
31
|
async with self.db_wrapper.reader_no_transaction() as conn:
|
|
32
|
--
cursor = await conn.execute("SELECT
|
|
32
|
++
cursor = await conn.execute("SELECT value from key_val_store WHERE key=?", (key,))
|
|
33
33
|
row = await cursor.fetchone()
|
|
34
34
|
await cursor.close()
|
|
35
35
|
|
|
36
36
|
if row is None:
|
|
37
37
|
return None
|
|
38
38
|
|
|
39
|
--
return object_type.from_bytes(row[
|
|
39
|
++
return object_type.from_bytes(row[0])
|
|
40
40
|
|
|
41
41
|
async def set_object(self, key: str, obj: Any):
|
|
42
42
|
"""
|
|
@@@ -1,7 -1,7 +1,8 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
--
from
|
|
4
|
++
from enum import Enum
|
|
5
|
++
from typing import Any, Dict, List, Optional
|
|
5
6
|
|
|
6
7
|
from chia.types.blockchain_format.program import Program
|
|
7
8
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
@@@ -9,6 -9,6 +10,12 @@@ from chia.util.ints import uint6
|
|
|
9
10
|
from chia.util.streamable import Streamable, streamable
|
|
10
11
|
|
|
11
12
|
|
|
13
|
++
class LineageProofField(Enum):
|
|
14
|
++
PARENT_NAME = 1
|
|
15
|
++
INNER_PUZZLE_HASH = 2
|
|
16
|
++
AMOUNT = 3
|
|
17
|
++
|
|
18
|
++
|
|
12
19
|
@streamable
|
|
13
20
|
@dataclass(frozen=True)
|
|
14
21
|
class LineageProof(Streamable):
|
|
@@@ -16,6 -16,6 +23,27 @@@
|
|
|
16
23
|
inner_puzzle_hash: Optional[bytes32] = None
|
|
17
24
|
amount: Optional[uint64] = None
|
|
18
25
|
|
|
26
|
++
@classmethod
|
|
27
|
++
def from_program(cls, program: Program, fields: List[LineageProofField]) -> LineageProof:
|
|
28
|
++
lineage_proof_info: Dict[str, Any] = {}
|
|
29
|
++
field_iter = iter(fields)
|
|
30
|
++
program_iter = program.as_iter()
|
|
31
|
++
for program_value in program_iter:
|
|
32
|
++
field = next(field_iter)
|
|
33
|
++
if field == LineageProofField.PARENT_NAME:
|
|
34
|
++
lineage_proof_info["parent_name"] = bytes32(program_value.as_atom())
|
|
35
|
++
elif field == LineageProofField.INNER_PUZZLE_HASH:
|
|
36
|
++
lineage_proof_info["inner_puzzle_hash"] = bytes32(program_value.as_atom())
|
|
37
|
++
elif field == LineageProofField.AMOUNT:
|
|
38
|
++
lineage_proof_info["amount"] = uint64(program_value.as_int())
|
|
39
|
++
try:
|
|
40
|
++
next(field_iter)
|
|
41
|
++
raise ValueError("Mismatch between program data and fields information")
|
|
42
|
++
except StopIteration:
|
|
43
|
++
pass
|
|
44
|
++
|
|
45
|
++
return LineageProof(**lineage_proof_info)
|
|
46
|
++
|
|
19
47
|
def to_program(self) -> Program:
|
|
20
48
|
final_list: List[Any] = []
|
|
21
49
|
if self.parent_name is not None:
|
|
@@@ -217,7 -217,7 +217,7 @@@ class GenesisByIdOrSingleton(Limitation
|
|
|
217
217
|
def match(uncurried_mod: Program, curried_args: Program) -> Tuple[bool, List[Program]]: # pragma: no cover
|
|
218
218
|
if uncurried_mod == GENESIS_BY_ID_OR_SINGLETON_MOD:
|
|
219
219
|
genesis_id = curried_args.first()
|
|
220
|
--
return True, [genesis_id
|
|
220
|
++
return True, [genesis_id]
|
|
221
221
|
else:
|
|
222
222
|
return False, []
|
|
223
223
|
|
|
@@@ -540,7 -540,7 +540,7 @@@ class TradeManager
|
|
|
540
540
|
fee_left_to_pay: uint64 = fee
|
|
541
541
|
# The access of the sorted keys here makes sure we create the XCH transaction first to make sure we pay fee
|
|
542
542
|
# with the XCH side of the offer and don't create an extra fee transaction in other wallets.
|
|
543
|
--
for id in sorted(coins_to_offer.keys()):
|
|
543
|
++
for id in sorted(coins_to_offer.keys(), key=lambda id: id != 1):
|
|
544
544
|
selected_coins = coins_to_offer[id]
|
|
545
545
|
if isinstance(id, int):
|
|
546
546
|
wallet = self.wallet_state_manager.wallets[id]
|
|
@@@ -47,6 -47,6 +47,8 @@@ def build_merkle_tree_from_binary_tree(
|
|
|
47
47
|
|
|
48
48
|
def list_to_binary_tree(objects: List[Any]) -> Any:
|
|
49
49
|
size = len(objects)
|
|
50
|
++
if size == 0:
|
|
51
|
++
raise ValueError("Cannot build a tree out of 0 objects")
|
|
50
52
|
if size == 1:
|
|
51
53
|
return objects[0]
|
|
52
54
|
midpoint = (size + 1) >> 1
|
|
@@@ -41,7 -41,7 +41,7 @@@ class PeerRequestCache
|
|
|
41
41
|
if header_block.is_transaction_block:
|
|
42
42
|
assert header_block.foliage_transaction_block is not None
|
|
43
43
|
if self._timestamps.get(header_block.height) is None:
|
|
44
|
--
self._timestamps.put(header_block.height, header_block.foliage_transaction_block.timestamp)
|
|
44
|
++
self._timestamps.put(header_block.height, uint64(header_block.foliage_transaction_block.timestamp))
|
|
45
45
|
|
|
46
46
|
def get_block_request(self, start: uint32, end: uint32) -> Optional[asyncio.Task[Any]]:
|
|
47
47
|
return self._block_requests.get((start, end))
|
|
@@@ -16,7 -16,7 +16,7 @@@ from chia.util.ints import uint16, uint
|
|
|
16
16
|
from chia.util.streamable import Streamable, streamable
|
|
17
17
|
from chia.wallet.cat_wallet.cat_utils import CAT_MOD, construct_cat_puzzle
|
|
18
18
|
from chia.wallet.conditions import AssertCoinAnnouncement
|
|
19
|
--
from chia.wallet.lineage_proof import LineageProof
|
|
19
|
++
from chia.wallet.lineage_proof import LineageProof, LineageProofField
|
|
20
20
|
from chia.wallet.payment import Payment
|
|
21
21
|
from chia.wallet.puzzles.load_clvm import load_clvm_maybe_recompile
|
|
22
22
|
from chia.wallet.puzzles.singleton_top_layer_v1_1 import SINGLETON_LAUNCHER_HASH, SINGLETON_MOD_HASH
|
|
@@@ -314,10 -314,10 +314,14 @@@ class CRCAT
|
|
|
314
314
|
uncurried_puzzle: UncurriedPuzzle = uncurry_puzzle(spend.puzzle_reveal.to_program())
|
|
315
315
|
first_uncurried_cr_layer: UncurriedPuzzle = uncurry_puzzle(uncurried_puzzle.args.at("rrf"))
|
|
316
316
|
second_uncurried_cr_layer: UncurriedPuzzle = uncurry_puzzle(first_uncurried_cr_layer.mod)
|
|
317
|
++
lineage_proof = LineageProof.from_program(
|
|
318
|
++
spend.solution.to_program().at("rf"),
|
|
319
|
++
[LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
|
|
320
|
++
)
|
|
317
321
|
return CRCAT(
|
|
318
322
|
spend.coin,
|
|
319
323
|
bytes32(uncurried_puzzle.args.at("rf").as_atom()),
|
|
320
|
--
|
|
324
|
++
lineage_proof,
|
|
321
325
|
[bytes32(ap.as_atom()) for ap in second_uncurried_cr_layer.args.at("rf").as_iter()],
|
|
322
326
|
second_uncurried_cr_layer.args.at("rrf"),
|
|
323
327
|
first_uncurried_cr_layer.args.at("rf").get_tree_hash(),
|
|
@@@ -359,6 -359,6 +363,7 @@@
|
|
|
359
363
|
raise ValueError(
|
|
360
364
|
"Previous spend was not a CR-CAT, nor did it properly remark the CR params"
|
|
361
365
|
) # pragma: no cover
|
|
366
|
++
authorized_providers = [bytes32(p.as_atom()) for p in authorized_providers_as_prog.as_iter()]
|
|
362
367
|
lineage_inner_puzhash: bytes32 = potential_cr_layer.get_tree_hash()
|
|
363
368
|
else:
|
|
364
369
|
# Otherwise the info we need will be in the puzzle reveal
|
|
@@@ -369,14 -369,14 +374,14 @@@
|
|
|
369
374
|
if conditions is None:
|
|
370
375
|
conditions = inner_puzzle.run(inner_solution)
|
|
371
376
|
inner_puzzle_hash: bytes32 = inner_puzzle.get_tree_hash()
|
|
377
|
++
authorized_providers = [bytes32(p.as_atom()) for p in authorized_providers_as_prog.as_iter()]
|
|
372
378
|
lineage_inner_puzhash = construct_cr_layer(
|
|
373
|
--
|
|
379
|
++
authorized_providers,
|
|
374
380
|
proofs_checker,
|
|
375
381
|
inner_puzzle_hash, # type: ignore
|
|
376
382
|
).get_tree_hash_precalc(inner_puzzle_hash)
|
|
377
383
|
|
|
378
384
|
# Convert all of the old stuff into python
|
|
379
|
--
authorized_providers: List[bytes32] = [bytes32(p.as_atom()) for p in authorized_providers_as_prog.as_iter()]
|
|
380
385
|
new_lineage_proof: LineageProof = LineageProof(
|
|
381
386
|
parent_spend.coin.parent_coin_info,
|
|
382
387
|
lineage_inner_puzhash,
|
|
@@@ -237,16 -237,16 +237,10 @@@ def create_eml_covenant_morpher
|
|
|
237
237
|
|
|
238
238
|
|
|
239
239
|
def construct_exigent_metadata_layer(
|
|
240
|
--
metadata: Optional[
|
|
241
|
--
transfer_program: Program,
|
|
242
|
--
inner_puzzle: Program,
|
|
240
|
++
metadata: Optional[Program], transfer_program: Program, inner_puzzle: Program
|
|
243
241
|
) -> Program:
|
|
244
242
|
return EXTIGENT_METADATA_LAYER.curry(
|
|
245
|
--
EXTIGENT_METADATA_LAYER_HASH,
|
|
246
|
--
metadata,
|
|
247
|
--
transfer_program,
|
|
248
|
--
transfer_program.get_tree_hash(),
|
|
249
|
--
inner_puzzle,
|
|
243
|
++
EXTIGENT_METADATA_LAYER_HASH, metadata, transfer_program, transfer_program.get_tree_hash(), inner_puzzle
|
|
250
244
|
)
|
|
251
245
|
|
|
252
246
|
|
|
@@@ -99,8 -99,8 +99,8 @@@ class WalletBlockchain(BlockchainInterf
|
|
|
99
99
|
and block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters is not None
|
|
100
100
|
):
|
|
101
101
|
assert block.finished_sub_slots[0].challenge_chain.new_difficulty is not None # They both change together
|
|
102
|
--
sub_slot_iters
|
|
103
|
--
difficulty
|
|
102
|
++
sub_slot_iters = uint64(block.finished_sub_slots[0].challenge_chain.new_sub_slot_iters)
|
|
103
|
++
difficulty = uint64(block.finished_sub_slots[0].challenge_chain.new_difficulty)
|
|
104
104
|
else:
|
|
105
105
|
sub_slot_iters = self._sub_slot_iters
|
|
106
106
|
difficulty = self._difficulty
|
|
@@@ -164,7 -164,7 +164,7 @@@
|
|
|
164
164
|
if timestamp is not None:
|
|
165
165
|
self._latest_timestamp = timestamp
|
|
166
166
|
elif block.foliage_transaction_block is not None:
|
|
167
|
--
self._latest_timestamp = block.foliage_transaction_block.timestamp
|
|
167
|
++
self._latest_timestamp = uint64(block.foliage_transaction_block.timestamp)
|
|
168
168
|
log.info(f"Peak set to: {self._peak.height} timestamp: {self._latest_timestamp}")
|
|
169
169
|
|
|
170
170
|
async def get_peak_block(self) -> Optional[HeaderBlock]:
|
|
@@@ -1058,7 -1058,7 +1058,7 @@@ class WalletNode
|
|
|
1058
1058
|
self.log.debug(f"get_timestamp_for_height_from_peer use cached block for height {request_height}")
|
|
1059
1059
|
|
|
1060
1060
|
if block is not None and block.foliage_transaction_block is not None:
|
|
1061
|
--
return block.foliage_transaction_block.timestamp
|
|
1061
|
++
return uint64(block.foliage_transaction_block.timestamp)
|
|
1062
1062
|
|
|
1063
1063
|
request_height -= 1
|
|
1064
1064
|
|
|
@@@ -210,7 -210,7 +210,9 @@@ elif [ "$(uname)" = "Linux" ]; the
|
|
|
210
210
|
echo "Installing on Arch Linux."
|
|
211
211
|
case $(uname -m) in
|
|
212
212
|
x86_64|aarch64)
|
|
213
|
--
|
|
213
|
++
if ! pacman -Qs "^git$" > /dev/null || ! pacman -Qs "^openssl$" > /dev/null ; then
|
|
214
|
++
sudo pacman ${PACMAN_AUTOMATED} -S --needed git openssl
|
|
215
|
++
fi
|
|
214
216
|
;;
|
|
215
217
|
*)
|
|
216
218
|
echo "Incompatible CPU architecture. Must be x86_64 or aarch64."
|
|
@@@ -11,7 -11,7 +11,6 @@@ chia.plotting.manage
|
|
|
11
11
|
chia.plotting.util
|
|
12
12
|
chia.rpc.rpc_client
|
|
13
13
|
chia.rpc.util
|
|
14
|
--
chia.rpc.wallet_rpc_client
|
|
15
14
|
chia.simulator.full_node_simulator
|
|
16
15
|
chia.simulator.keyring
|
|
17
16
|
chia.simulator.wallet_tools
|
|
@@@ -19,10 -19,10 +18,8 @@@ chia.ssl.create_ss
|
|
|
19
18
|
chia.timelord.timelord_api
|
|
20
19
|
chia.timelord.timelord_launcher
|
|
21
20
|
chia.types.blockchain_format.program
|
|
22
|
--
chia.wallet.block_record
|
|
23
21
|
chia.wallet.chialisp
|
|
24
22
|
chia.wallet.did_wallet.did_wallet
|
|
25
|
--
chia.wallet.did_wallet.did_wallet_puzzles
|
|
26
23
|
chia.wallet.key_val_store
|
|
27
24
|
chia.wallet.lineage_proof
|
|
28
25
|
chia.wallet.nft_wallet.nft_puzzles
|
|
@@@ -105,25 -105,25 +102,16 @@@ tests.util.test_full_block_util
|
|
|
105
102
|
tests.util.test_misc
|
|
106
103
|
tests.util.test_network
|
|
107
104
|
tests.util.time_out_assert
|
|
108
|
--
tests.wallet.cat_wallet.test_cat_wallet
|
|
109
|
--
tests.wallet.cat_wallet.test_offer_lifecycle
|
|
110
105
|
tests.wallet.cat_wallet.test_trades
|
|
111
106
|
tests.wallet.did_wallet.test_did
|
|
112
|
--
tests.wallet.nft_wallet.test_nft_wallet
|
|
113
107
|
tests.wallet.rpc.test_wallet_rpc
|
|
114
108
|
tests.wallet.simple_sync.test_simple_sync_protocol
|
|
115
109
|
tests.wallet.sync.test_wallet_sync
|
|
116
|
--
tests.wallet.test_bech32m
|
|
117
110
|
tests.wallet.test_chialisp
|
|
118
|
--
tests.wallet.test_puzzle_store
|
|
119
|
--
tests.wallet.test_singleton
|
|
120
111
|
tests.wallet.test_singleton_lifecycle
|
|
121
112
|
tests.wallet.test_singleton_lifecycle_fast
|
|
122
113
|
tests.wallet.test_taproot
|
|
123
|
--
tests.wallet.test_wallet_blockchain
|
|
124
114
|
tests.wallet.test_wallet_interested_store
|
|
125
115
|
tests.wallet.test_wallet_key_val_store
|
|
126
|
--
tests.wallet.test_wallet_user_store
|
|
127
116
|
tools.analyze-chain
|
|
128
117
|
tools.run_block
|
|
129
|
--
tools.test_full_sync
|
|
@@@ -7,14 -7,14 +7,14 @@@ from setuptools import find_packages, s
|
|
|
7
7
|
|
|
8
8
|
dependencies = [
|
|
9
9
|
"aiofiles==23.2.1", # Async IO for files
|
|
10
|
--
"anyio==4.
|
|
11
|
--
"boto3==1.34.
|
|
10
|
++
"anyio==4.2.0",
|
|
11
|
++
"boto3==1.34.11", # AWS S3 for DL s3 plugin
|
|
12
12
|
"chiavdf==1.1.1", # timelord and vdf verification
|
|
13
13
|
"chiabip158==1.3", # bip158-style wallet filters
|
|
14
14
|
"chiapos==2.0.3", # proof of space
|
|
15
15
|
"clvm==0.9.8",
|
|
16
16
|
"clvm_tools==0.4.7", # Currying, Program.to, other conveniences
|
|
17
|
--
"chia_rs==0.
|
|
17
|
++
"chia_rs==0.4.0",
|
|
18
18
|
"clvm-tools-rs==0.1.40", # Rust implementation of clvm_tools' compiler
|
|
19
19
|
"aiohttp==3.9.1", # HTTP server for full node rpc
|
|
20
20
|
"aiosqlite==0.19.0", # asyncio wrapper for sqlite, to store blocks
|
|
@@@ -32,7 -32,7 +32,7 @@@
|
|
|
32
32
|
"dnspython==2.4.2", # Query DNS seeds
|
|
33
33
|
"watchdog==2.2.0", # Filesystem event watching - watches keyring.yaml
|
|
34
34
|
"dnslib==0.9.23", # dns lib
|
|
35
|
--
"typing-extensions==4.
|
|
35
|
++
"typing-extensions==4.9.0", # typing backports like Protocol and TypedDict
|
|
36
36
|
"zstd==1.5.5.1",
|
|
37
37
|
"packaging==23.2",
|
|
38
38
|
"psutil==5.9.4",
|
|
@@@ -49,25 -49,25 +49,25 @@@ dev_dependencies =
|
|
|
49
49
|
"diff-cover==8.0.1",
|
|
50
50
|
"pre-commit==3.5.0; python_version < '3.9'",
|
|
51
51
|
"pre-commit==3.6.0; python_version >= '3.9'",
|
|
52
|
--
"py3createtorrent==1.
|
|
53
|
--
"pylint==3.0.
|
|
54
|
--
"pytest==7.4.
|
|
52
|
++
"py3createtorrent==1.2.0",
|
|
53
|
++
"pylint==3.0.3",
|
|
54
|
++
"pytest==7.4.4",
|
|
55
55
|
"pytest-cov==4.1.0",
|
|
56
56
|
"pytest-mock==3.12.0",
|
|
57
57
|
"pytest-xdist==3.5.0",
|
|
58
58
|
"pyupgrade==3.15.0",
|
|
59
59
|
"twine==4.0.2",
|
|
60
|
--
"isort==5.
|
|
61
|
--
"flake8==
|
|
62
|
--
"mypy==1.
|
|
63
|
--
"black==23.
|
|
60
|
++
"isort==5.13.2",
|
|
61
|
++
"flake8==7.0.0",
|
|
62
|
++
"mypy==1.8.0",
|
|
63
|
++
"black==23.12.1",
|
|
64
64
|
"lxml==4.9.3",
|
|
65
65
|
"aiohttp_cors==0.7.0", # For blackd
|
|
66
|
--
"pyinstaller==
|
|
67
|
--
"types-aiofiles==23.2.0.
|
|
66
|
++
"pyinstaller==6.3.0",
|
|
67
|
++
"types-aiofiles==23.2.0.20240106",
|
|
68
68
|
"types-cryptography==3.3.23.2",
|
|
69
69
|
"types-pyyaml==6.0.12.12",
|
|
70
|
--
"types-setuptools==69.0.0.
|
|
70
|
++
"types-setuptools==69.0.0.20240115",
|
|
71
71
|
]
|
|
72
72
|
|
|
73
73
|
legacy_keyring_dependencies = [
|
|
@@@ -576,10 -576,10 +576,9 @@@ class TestBlockHeaderValidation
|
|
|
576
576
|
block.finished_sub_slots[-1],
|
|
577
577
|
"infused_challenge_chain",
|
|
578
578
|
InfusedChallengeChainSubSlot(
|
|
579
|
--
|
|
580
|
--
|
|
581
|
--
|
|
582
|
--
].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf,
|
|
579
|
++
block.finished_sub_slots[
|
|
580
|
++
-1
|
|
581
|
++
].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
|
|
583
582
|
number_of_iterations=uint64(10000000),
|
|
584
583
|
)
|
|
585
584
|
),
|
|
@@@ -594,10 -594,10 +593,9 @@@
|
|
|
594
593
|
block.finished_sub_slots[-1],
|
|
595
594
|
"infused_challenge_chain",
|
|
596
595
|
InfusedChallengeChainSubSlot(
|
|
597
|
--
|
|
598
|
--
|
|
599
|
--
|
|
600
|
--
].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf,
|
|
596
|
++
block.finished_sub_slots[
|
|
597
|
++
-1
|
|
598
|
++
].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
|
|
601
599
|
output=ClassgroupElement.get_default_element(),
|
|
602
600
|
)
|
|
603
601
|
),
|
|
@@@ -613,11 -613,11 +611,10 @@@
|
|
|
613
611
|
block.finished_sub_slots[-1],
|
|
614
612
|
"infused_challenge_chain",
|
|
615
613
|
InfusedChallengeChainSubSlot(
|
|
616
|
--
|
|
617
|
--
|
|
618
|
--
|
|
619
|
--
]
|
|
620
|
--
challenge=bytes32([0] * 32),
|
|
614
|
++
block.finished_sub_slots[
|
|
615
|
++
-1
|
|
616
|
++
].infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.replace(
|
|
617
|
++
challenge=bytes32([0] * 32)
|
|
621
618
|
)
|
|
622
619
|
),
|
|
623
620
|
)
|
|
@@@ -660,8 -660,8 +657,7 @@@
|
|
|
660
657
|
new_finished_ss = recursive_replace(
|
|
661
658
|
block.finished_sub_slots[-1],
|
|
662
659
|
"challenge_chain",
|
|
663
|
--
replace(
|
|
664
|
--
block.finished_sub_slots[-1].challenge_chain,
|
|
660
|
++
block.finished_sub_slots[-1].challenge_chain.replace(
|
|
665
661
|
infused_challenge_chain_sub_slot_hash=bytes([1] * 32),
|
|
666
662
|
),
|
|
667
663
|
)
|
|
@@@ -671,8 -671,8 +667,7 @@@
|
|
|
671
667
|
new_finished_ss = recursive_replace(
|
|
672
668
|
block.finished_sub_slots[-1],
|
|
673
669
|
"challenge_chain",
|
|
674
|
--
replace(
|
|
675
|
--
block.finished_sub_slots[-1].challenge_chain,
|
|
670
|
++
block.finished_sub_slots[-1].challenge_chain.replace(
|
|
676
671
|
infused_challenge_chain_sub_slot_hash=block.finished_sub_slots[
|
|
677
672
|
-1
|
|
678
673
|
].infused_challenge_chain.get_hash(),
|
|
@@@ -698,7 -698,7 +693,7 @@@
|
|
|
698
693
|
new_finished_ss_bad_rc = recursive_replace(
|
|
699
694
|
block.finished_sub_slots[-1],
|
|
700
695
|
"reward_chain",
|
|
701
|
--
|
|
696
|
++
block.finished_sub_slots[-1].reward_chain.replace(infused_challenge_chain_sub_slot_hash=None),
|
|
702
697
|
)
|
|
703
698
|
block_bad = recursive_replace(
|
|
704
699
|
block, "finished_sub_slots", block.finished_sub_slots[:-1] + [new_finished_ss_bad_rc]
|
|
@@@ -746,7 -746,7 +741,7 @@@
|
|
|
746
741
|
new_finished_ss = recursive_replace(
|
|
747
742
|
blocks[-1].finished_sub_slots[-1],
|
|
748
743
|
"challenge_chain",
|
|
749
|
--
|
|
744
|
++
blocks[-1].finished_sub_slots[-1].challenge_chain.replace(subepoch_summary_hash=std_hash(b"0")),
|
|
750
745
|
)
|
|
751
746
|
block_bad = recursive_replace(
|
|
752
747
|
blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
|
|
@@@ -794,7 -794,7 +789,7 @@@
|
|
|
794
789
|
new_finished_ss = recursive_replace(
|
|
795
790
|
blocks[-1].finished_sub_slots[-1],
|
|
796
791
|
"reward_chain",
|
|
797
|
--
|
|
792
|
++
blocks[-1].finished_sub_slots[-1].reward_chain.replace(challenge_chain_sub_slot_hash=bytes([3] * 32)),
|
|
798
793
|
)
|
|
799
794
|
block_1_bad = recursive_replace(
|
|
800
795
|
blocks[-1], "finished_sub_slots", blocks[-1].finished_sub_slots[:-1] + [new_finished_ss]
|
|
@@@ -1040,8 -1040,8 +1035,8 @@@
|
|
|
1040
1035
|
new_finished_ss = recursive_replace(
|
|
1041
1036
|
new_finished_ss,
|
|
1042
1037
|
"reward_chain",
|
|
1043
|
--
replace(
|
|
1044
|
--
|
|
1038
|
++
new_finished_ss.reward_chain.replace(
|
|
1039
|
++
challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash()
|
|
1045
1040
|
),
|
|
1046
1041
|
)
|
|
1047
1042
|
block_bad = recursive_replace(block, "finished_sub_slots", [new_finished_ss] + block.finished_sub_slots[1:])
|
|
@@@ -1075,8 -1075,8 +1070,7 @@@
|
|
|
1075
1070
|
new_finished_ss = recursive_replace(
|
|
1076
1071
|
new_finished_ss,
|
|
1077
1072
|
"reward_chain",
|
|
1078
|
--
replace(
|
|
1079
|
--
new_finished_ss.reward_chain,
|
|
1073
|
++
new_finished_ss.reward_chain.replace(
|
|
1080
1074
|
challenge_chain_sub_slot_hash=new_finished_ss.challenge_chain.get_hash(),
|
|
1081
1075
|
),
|
|
1082
1076
|
)
|
|
@@@ -5,12 -5,12 +5,12 @@@ from typing import Dict, Lis
|
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
|
--
from benchmarks.utils import rand_hash
|
|
9
8
|
from chia.consensus.block_record import BlockRecord
|
|
10
9
|
from chia.consensus.blockchain_interface import BlockchainInterface
|
|
11
10
|
from chia.consensus.find_fork_point import find_fork_point_in_chain, lookup_fork_chain
|
|
12
11
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
13
12
|
from chia.util.ints import uint32
|
|
13
|
++
from tests.util.benchmarks import rand_hash
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class DummyChain:
|
|
@@@ -129,8 -129,8 +129,8 @@@ for path in test_paths
|
|
|
129
129
|
# TODO: design a configurable system for this
|
|
130
130
|
process_count = {
|
|
131
131
|
"macos": {False: 0, True: 4}.get(conf["parallel"], conf["parallel"]),
|
|
132
|
--
"ubuntu": {False: 0, True:
|
|
133
|
--
"windows": {False: 0, True:
|
|
132
|
++
"ubuntu": {False: 0, True: 6}.get(conf["parallel"], conf["parallel"]),
|
|
133
|
++
"windows": {False: 0, True: 6}.get(conf["parallel"], conf["parallel"]),
|
|
134
134
|
}
|
|
135
135
|
pytest_parallel_args = {os: f" -n {count}" for os, count in process_count.items()}
|
|
136
136
|
|
|
@@@ -323,6 -323,6 +323,6 @@@ def test_did_transfer(capsys: object, g
|
|
|
323
323
|
]
|
|
324
324
|
run_cli_command_and_assert(capsys, root_dir, command_args, assert_list)
|
|
325
325
|
expected_calls: logType = {
|
|
326
|
--
"did_transfer_did": [(w_id, t_address,
|
|
326
|
++
"did_transfer_did": [(w_id, t_address, 500000000000, True, DEFAULT_TX_CONFIG.override(reuse_puzhash=True))],
|
|
327
327
|
}
|
|
328
328
|
test_rpc_clients.wallet_rpc_client.check_log(expected_calls)
|
|
@@@ -3,6 -3,6 +3,7 @@@ from __future__ import annotation
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
|
5
5
|
|
|
6
|
++
import pkg_resources
|
|
6
7
|
from chia_rs import Coin, G2Element
|
|
7
8
|
|
|
8
9
|
from chia.server.outbound_message import NodeType
|
|
@@@ -35,7 -35,7 +36,7 @@@ from tests.cmds.wallet.test_consts impo
|
|
|
35
36
|
get_bytes32,
|
|
36
37
|
)
|
|
37
38
|
|
|
38
|
--
test_offer_file_path: Path = Path(
|
|
39
|
++
test_offer_file_path: Path = Path(pkg_resources.resource_filename(__name__, "test_offer.toffer"))
|
|
39
40
|
test_offer_file_name: str = str(test_offer_file_path)
|
|
40
41
|
test_offer_file_bech32: str = open(test_offer_file_name).read()
|
|
41
42
|
test_offer_id: str = "0xdfb7e8643376820ec995b0bcdb3fc1f764c16b814df5e074631263fcf1e00839"
|
|
@@@ -696,6 -696,6 +697,7 @@@ def test_make_offer(capsys: object, get
|
|
|
696
697
|
"Including Fees: 1 XCH, 1000000000000 mojos",
|
|
697
698
|
"Created offer with ID 0202020202020202020202020202020202020202020202020202020202020202",
|
|
698
699
|
]
|
|
700
|
++
run_cli_command_and_assert(capsys, root_dir, command_args[:-4], ["without --override"])
|
|
699
701
|
run_cli_command_and_assert(capsys, root_dir, command_args, assert_list)
|
|
700
702
|
expected_calls: logType = {
|
|
701
703
|
"cat_asset_id_to_name": [(request_cat_id,)],
|
|
@@@ -2292,3 -2292,3 +2292,39 @@@ async def test_wallet_log_in_changes_ac
|
|
|
2292
2292
|
|
|
2293
2293
|
active_fingerprint = cast(int, (await wallet_rpc_api.get_logged_in_fingerprint(request={}))["fingerprint"])
|
|
2294
2294
|
assert active_fingerprint == secondary_fingerprint
|
|
2295
|
++
|
|
2296
|
++
|
|
2297
|
++
@pytest.mark.limit_consensus_modes(reason="does not depend on consensus rules")
|
|
2298
|
++
@pytest.mark.anyio
|
|
2299
|
++
async def test_mirrors(
|
|
2300
|
++
self_hostname: str, one_wallet_and_one_simulator_services: SimulatorsAndWalletsServices, tmp_path: Path
|
|
2301
|
++
) -> None:
|
|
2302
|
++
wallet_rpc_api, full_node_api, wallet_rpc_port, ph, bt = await init_wallet_and_node(
|
|
2303
|
++
self_hostname, one_wallet_and_one_simulator_services
|
|
2304
|
++
)
|
|
2305
|
++
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
|
2306
|
++
data_rpc_api = DataLayerRpcApi(data_layer)
|
|
2307
|
++
res = await data_rpc_api.create_data_store({})
|
|
2308
|
++
assert res is not None
|
|
2309
|
++
store_id = bytes32(hexstr_to_bytes(res["id"]))
|
|
2310
|
++
await farm_block_check_singleton(data_layer, full_node_api, ph, store_id, wallet=wallet_rpc_api.service)
|
|
2311
|
++
|
|
2312
|
++
urls = ["http://127.0.0.1/8000", "http://127.0.0.1/8001"]
|
|
2313
|
++
res = await data_rpc_api.add_mirror({"id": store_id.hex(), "urls": urls, "amount": 1, "fee": 1})
|
|
2314
|
++
|
|
2315
|
++
await farm_block_check_singleton(data_layer, full_node_api, ph, store_id, wallet=wallet_rpc_api.service)
|
|
2316
|
++
mirrors = await data_rpc_api.get_mirrors({"id": store_id.hex()})
|
|
2317
|
++
mirror_list = mirrors["mirrors"]
|
|
2318
|
++
assert len(mirror_list) == 1
|
|
2319
|
++
mirror = mirror_list[0]
|
|
2320
|
++
assert mirror["urls"] == ["http://127.0.0.1/8000", "http://127.0.0.1/8001"]
|
|
2321
|
++
coin_id = mirror["coin_id"]
|
|
2322
|
++
|
|
2323
|
++
res = await data_rpc_api.delete_mirror({"coin_id": coin_id, "fee": 1})
|
|
2324
|
++
await farm_block_check_singleton(data_layer, full_node_api, ph, store_id, wallet=wallet_rpc_api.service)
|
|
2325
|
++
mirrors = await data_rpc_api.get_mirrors({"id": store_id.hex()})
|
|
2326
|
++
mirror_list = mirrors["mirrors"]
|
|
2327
|
++
assert len(mirror_list) == 0
|
|
2328
|
++
|
|
2329
|
++
with pytest.raises(RuntimeError, match="URL list can't be empty"):
|
|
2330
|
++
res = await data_rpc_api.add_mirror({"id": store_id.hex(), "urls": [], "amount": 1, "fee": 1})
|
|
@@@ -308,7 -308,7 +308,7 @@@ async def test_get_ancestors_optimized(
|
|
|
308
308
|
if i > 25 and i <= 200 and random.randint(0, 4):
|
|
309
309
|
is_insert = True
|
|
310
310
|
if i > 200:
|
|
311
|
--
hint_keys_values = await data_store.
|
|
311
|
++
hint_keys_values = await data_store.get_keys_values_compressed(tree_id)
|
|
312
312
|
if not deleted_all:
|
|
313
313
|
while node_count > 0:
|
|
314
314
|
node_count -= 1
|
|
@@@ -383,7 -383,7 +383,7 @@@ async def test_batch_update(data_store
|
|
|
383
383
|
|
|
384
384
|
batch: List[Dict[str, Any]] = []
|
|
385
385
|
keys_values: Dict[bytes, bytes] = {}
|
|
386
|
--
hint_keys_values: Optional[Dict[
|
|
386
|
++
hint_keys_values: Optional[Dict[bytes32, bytes32]] = {} if use_optimized else None
|
|
387
387
|
for operation in range(num_batches * num_ops_per_batch):
|
|
388
388
|
[op_type] = random.choices(
|
|
389
389
|
["insert", "upsert-insert", "upsert-update", "delete"],
|
|
@@@ -490,7 -490,7 +490,7 @@@ async def test_upsert_ignores_existing_
|
|
|
490
490
|
) -> None:
|
|
491
491
|
key = b"key"
|
|
492
492
|
value = b"value1"
|
|
493
|
--
hint_keys_values: Optional[Dict[
|
|
493
|
++
hint_keys_values: Optional[Dict[bytes32, bytes32]] = {} if use_optimized else None
|
|
494
494
|
|
|
495
495
|
await data_store.autoinsert(
|
|
496
496
|
key=key,
|
|
@@@ -643,7 -643,7 +643,7 @@@ async def test_inserting_duplicate_key_
|
|
|
643
643
|
side=Side.RIGHT,
|
|
644
644
|
)
|
|
645
645
|
|
|
646
|
--
hint_keys_values = await data_store.
|
|
646
|
++
hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
|
|
647
647
|
# TODO: more specific exception
|
|
648
648
|
with pytest.raises(Exception):
|
|
649
649
|
await data_store.insert(
|
|
@@@ -691,7 -691,7 +691,7 @@@ async def test_inserting_invalid_length
|
|
|
691
691
|
async def test_autoinsert_balances_from_scratch(data_store: DataStore, tree_id: bytes32) -> None:
|
|
692
692
|
random = Random()
|
|
693
693
|
random.seed(100, version=2)
|
|
694
|
--
hint_keys_values: Dict[
|
|
694
|
++
hint_keys_values: Dict[bytes32, bytes32] = {}
|
|
695
695
|
hashes = []
|
|
696
696
|
|
|
697
697
|
for i in range(2000):
|
|
@@@ -710,7 -710,7 +710,7 @@@
|
|
|
710
710
|
async def test_autoinsert_balances_gaps(data_store: DataStore, tree_id: bytes32) -> None:
|
|
711
711
|
random = Random()
|
|
712
712
|
random.seed(101, version=2)
|
|
713
|
--
hint_keys_values: Dict[
|
|
713
|
++
hint_keys_values: Dict[bytes32, bytes32] = {}
|
|
714
714
|
hashes = []
|
|
715
715
|
|
|
716
716
|
for i in range(2000):
|
|
@@@ -749,7 -749,7 +749,7 @@@ async def test_delete_from_left_both_te
|
|
|
749
749
|
|
|
750
750
|
hint_keys_values = None
|
|
751
751
|
if use_hint:
|
|
752
|
--
hint_keys_values = await data_store.
|
|
752
|
++
hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
|
|
753
753
|
|
|
754
754
|
expected = Program.to(
|
|
755
755
|
(
|
|
@@@ -789,7 -789,7 +789,7 @@@ async def test_delete_from_left_other_n
|
|
|
789
789
|
|
|
790
790
|
hint_keys_values = None
|
|
791
791
|
if use_hint:
|
|
792
|
--
hint_keys_values = await data_store.
|
|
792
|
++
hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
|
|
793
793
|
|
|
794
794
|
expected = Program.to(
|
|
795
795
|
(
|
|
@@@ -827,7 -827,7 +827,7 @@@ async def test_delete_from_right_both_t
|
|
|
827
827
|
|
|
828
828
|
hint_keys_values = None
|
|
829
829
|
if use_hint:
|
|
830
|
--
hint_keys_values = await data_store.
|
|
830
|
++
hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
|
|
831
831
|
|
|
832
832
|
expected = Program.to(
|
|
833
833
|
(
|
|
@@@ -867,7 -867,7 +867,7 @@@ async def test_delete_from_right_other_
|
|
|
867
867
|
|
|
868
868
|
hint_keys_values = None
|
|
869
869
|
if use_hint:
|
|
870
|
--
hint_keys_values = await data_store.
|
|
870
|
++
hint_keys_values = await data_store.get_keys_values_compressed(tree_id=tree_id)
|
|
871
871
|
|
|
872
872
|
expected = Program.to(
|
|
873
873
|
(
|
|
@@@ -1208,6 -1208,6 +1208,33 @@@ async def test_kv_diff_2(data_store: Da
|
|
|
1208
1208
|
assert diff_3 == set()
|
|
1209
1209
|
|
|
1210
1210
|
|
|
1211
|
++
@pytest.mark.anyio
|
|
1212
|
++
async def test_kv_diff_3(data_store: DataStore, tree_id: bytes32) -> None:
|
|
1213
|
++
insert_result = await data_store.autoinsert(
|
|
1214
|
++
key=b"000",
|
|
1215
|
++
value=b"000",
|
|
1216
|
++
tree_id=tree_id,
|
|
1217
|
++
status=Status.COMMITTED,
|
|
1218
|
++
)
|
|
1219
|
++
await data_store.delete(tree_id=tree_id, key=b"000", status=Status.COMMITTED)
|
|
1220
|
++
insert_result_2 = await data_store.autoinsert(
|
|
1221
|
++
key=b"000",
|
|
1222
|
++
value=b"001",
|
|
1223
|
++
tree_id=tree_id,
|
|
1224
|
++
status=Status.COMMITTED,
|
|
1225
|
++
)
|
|
1226
|
++
diff_1 = await data_store.get_kv_diff(tree_id, insert_result.node_hash, insert_result_2.node_hash)
|
|
1227
|
++
assert diff_1 == {DiffData(OperationType.DELETE, b"000", b"000"), DiffData(OperationType.INSERT, b"000", b"001")}
|
|
1228
|
++
insert_result_3 = await data_store.upsert(
|
|
1229
|
++
key=b"000",
|
|
1230
|
++
new_value=b"002",
|
|
1231
|
++
tree_id=tree_id,
|
|
1232
|
++
status=Status.COMMITTED,
|
|
1233
|
++
)
|
|
1234
|
++
diff_2 = await data_store.get_kv_diff(tree_id, insert_result_2.node_hash, insert_result_3.node_hash)
|
|
1235
|
++
assert diff_2 == {DiffData(OperationType.DELETE, b"000", b"001"), DiffData(OperationType.INSERT, b"000", b"002")}
|
|
1236
|
++
|
|
1237
|
++
|
|
1211
1238
|
@pytest.mark.anyio
|
|
1212
1239
|
async def test_rollback_to_generation(data_store: DataStore, tree_id: bytes32) -> None:
|
|
1213
1240
|
await add_0123_example(data_store, tree_id)
|
|
@@@ -111,7 -111,7 +111,7 @@@ async def test_basic_coin_store(db_vers
|
|
|
111
111
|
assert block.foliage_transaction_block is not None
|
|
112
112
|
await coin_store.new_block(
|
|
113
113
|
block.height,
|
|
114
|
--
block.foliage_transaction_block.timestamp,
|
|
114
|
++
uint64(block.foliage_transaction_block.timestamp),
|
|
115
115
|
block.get_included_reward_coins(),
|
|
116
116
|
tx_additions,
|
|
117
117
|
tx_removals,
|
|
@@@ -121,7 -121,7 +121,7 @@@
|
|
|
121
121
|
with pytest.raises(Exception):
|
|
122
122
|
await coin_store.new_block(
|
|
123
123
|
block.height,
|
|
124
|
--
block.foliage_transaction_block.timestamp,
|
|
124
|
++
uint64(block.foliage_transaction_block.timestamp),
|
|
125
125
|
block.get_included_reward_coins(),
|
|
126
126
|
tx_additions,
|
|
127
127
|
tx_removals,
|
|
@@@ -179,7 -179,7 +179,7 @@@ async def test_set_spent(db_version: in
|
|
|
179
179
|
assert block.foliage_transaction_block is not None
|
|
180
180
|
await coin_store.new_block(
|
|
181
181
|
block.height,
|
|
182
|
--
block.foliage_transaction_block.timestamp,
|
|
182
|
++
uint64(block.foliage_transaction_block.timestamp),
|
|
183
183
|
block.get_included_reward_coins(),
|
|
184
184
|
additions,
|
|
185
185
|
removals,
|
|
@@@ -227,7 -227,7 +227,7 @@@ async def test_num_unspent(bt: BlockToo
|
|
|
227
227
|
additions: List[Coin] = []
|
|
228
228
|
await coin_store.new_block(
|
|
229
229
|
block.height,
|
|
230
|
--
block.foliage_transaction_block.timestamp,
|
|
230
|
++
uint64(block.foliage_transaction_block.timestamp),
|
|
231
231
|
block.get_included_reward_coins(),
|
|
232
232
|
additions,
|
|
233
233
|
removals,
|
|
@@@ -259,7 -259,7 +259,7 @@@ async def test_rollback(db_version: int
|
|
|
259
259
|
assert block.foliage_transaction_block is not None
|
|
260
260
|
await coin_store.new_block(
|
|
261
261
|
block.height,
|
|
262
|
--
block.foliage_transaction_block.timestamp,
|
|
262
|
++
uint64(block.foliage_transaction_block.timestamp),
|
|
263
263
|
block.get_included_reward_coins(),
|
|
264
264
|
additions,
|
|
265
265
|
removals,
|
|
@@@ -114,7 -114,7 +114,9 @@@ async def test_basic_store
|
|
|
114
114
|
h_hash_1 = bytes32.random(seeded_random)
|
|
115
115
|
assert not store.seen_unfinished_block(h_hash_1)
|
|
116
116
|
assert store.seen_unfinished_block(h_hash_1)
|
|
117
|
--
|
|
117
|
++
# this will crowd out h_hash_1
|
|
118
|
++
for _ in range(store.max_seen_unfinished_blocks):
|
|
119
|
++
store.seen_unfinished_block(bytes32.random(seeded_random))
|
|
118
120
|
assert not store.seen_unfinished_block(h_hash_1)
|
|
119
121
|
|
|
120
122
|
# Add/get unfinished block
|
|
@@@ -501,7 -501,7 +503,7 @@@
|
|
|
501
503
|
blocks[1].reward_chain_sp_proof,
|
|
502
504
|
)
|
|
503
505
|
assert not store.new_signage_point(
|
|
504
|
--
blocks[1].reward_chain_block.signage_point_index,
|
|
506
|
++
uint8(blocks[1].reward_chain_block.signage_point_index),
|
|
505
507
|
blockchain,
|
|
506
508
|
peak,
|
|
507
509
|
uint64(blockchain.block_record(blocks[1].header_hash).sp_sub_slot_total_iters(custom_block_tools.constants)),
|
|
@@@ -803,9 -803,9 +805,9 @@@
|
|
|
803
805
|
|
|
804
806
|
blocks = custom_block_tools.get_consecutive_blocks(2, block_list_input=blocks, guarantee_transaction_block=True)
|
|
805
807
|
|
|
806
|
--
i3 = blocks[-3].reward_chain_block.signage_point_index
|
|
807
|
--
i2 = blocks[-2].reward_chain_block.signage_point_index
|
|
808
|
--
i1 = blocks[-1].reward_chain_block.signage_point_index
|
|
808
|
++
i3 = uint8(blocks[-3].reward_chain_block.signage_point_index)
|
|
809
|
++
i2 = uint8(blocks[-2].reward_chain_block.signage_point_index)
|
|
810
|
++
i1 = uint8(blocks[-1].reward_chain_block.signage_point_index)
|
|
809
811
|
if (
|
|
810
812
|
len(blocks[-2].finished_sub_slots) == len(blocks[-1].finished_sub_slots) == 0
|
|
811
813
|
and not is_overflow_block(custom_block_tools.constants, signage_point_index=i2)
|
|
@@@ -63,8 -63,8 +63,8 @@@ from tests.conftest import ConsensusMod
|
|
|
63
63
|
from tests.connection_utils import add_dummy_connection, connect_and_get_peer
|
|
64
64
|
from tests.core.full_node.stores.test_coin_store import get_future_reward_coins
|
|
65
65
|
from tests.core.make_block_generator import make_spend_bundle
|
|
66
|
--
from tests.core.mempool.test_mempool_performance import wallet_height_at_least
|
|
67
66
|
from tests.core.node_height import node_height_at_least
|
|
67
|
++
from tests.util.misc import wallet_height_at_least
|
|
68
68
|
from tests.util.setup_nodes import SimulatorsAndWalletsServices
|
|
69
69
|
from tests.util.time_out_assert import time_out_assert, time_out_assert_custom_interval, time_out_messages
|
|
70
70
|
|
|
@@@ -1586,12 -1586,12 +1586,10 @@@ class TestFullNodeProtocol
|
|
|
1586
1586
|
# Submit the sub slot, but not the last block
|
|
1587
1587
|
blocks = bt.get_consecutive_blocks(1, block_list_input=blocks, skip_slots=1, force_overflow=True)
|
|
1588
1588
|
for ss in blocks[-1].finished_sub_slots:
|
|
1589
|
--
challenge_chain =
|
|
1590
|
--
ss.challenge_chain,
|
|
1589
|
++
challenge_chain = ss.challenge_chain.replace(
|
|
1591
1590
|
new_difficulty=20,
|
|
1592
1591
|
)
|
|
1593
|
--
slot2 =
|
|
1594
|
--
ss,
|
|
1592
|
++
slot2 = ss.replace(
|
|
1595
1593
|
challenge_chain=challenge_chain,
|
|
1596
1594
|
)
|
|
1597
1595
|
await full_node_1.respond_end_of_sub_slot(fnp.RespondEndOfSubSlot(slot2), peer)
|
|
@@@ -20,21 -20,21 +20,21 @@@ ph4 = bytes32(b"h" * 32
|
|
|
20
20
|
def test_has_ph_sub() -> None:
|
|
21
21
|
sub = PeerSubscriptions()
|
|
22
22
|
|
|
23
|
--
assert sub.
|
|
24
|
--
assert sub.
|
|
23
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
24
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
25
25
|
|
|
26
|
--
ret = sub.
|
|
26
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1], 100)
|
|
27
27
|
assert ret == {ph1}
|
|
28
28
|
|
|
29
|
--
assert sub.
|
|
30
|
--
assert sub.
|
|
29
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
30
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
31
31
|
|
|
32
|
--
ret = sub.
|
|
32
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2], 100)
|
|
33
33
|
# we have already subscribed to ph1, it's filtered in the returned list
|
|
34
34
|
assert ret == {ph2}
|
|
35
35
|
|
|
36
|
--
assert sub.
|
|
37
|
--
assert sub.
|
|
36
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
37
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
38
38
|
|
|
39
39
|
# note that this is technically a type error as well.
|
|
40
40
|
# we can remove these asserts once we have type checking
|
|
@@@ -43,8 -43,8 +43,8 @@@
|
|
|
43
43
|
|
|
44
44
|
sub.remove_peer(peer1)
|
|
45
45
|
|
|
46
|
--
assert sub.
|
|
47
|
--
assert sub.
|
|
46
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
47
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
def test_has_coin_sub() -> None:
|
|
@@@ -65,8 -65,8 +65,8 @@@
|
|
|
65
65
|
|
|
66
66
|
# note that this is technically a type error as well.
|
|
67
67
|
# we can remove these asserts once we have type checking
|
|
68
|
--
assert sub.
|
|
69
|
--
assert sub.
|
|
68
|
++
assert sub.has_puzzle_subscription(coin1) is False
|
|
69
|
++
assert sub.has_puzzle_subscription(coin2) is False
|
|
70
70
|
|
|
71
71
|
sub.remove_peer(peer1)
|
|
72
72
|
|
|
@@@ -119,34 -119,34 +119,34 @@@ def test_overlapping_coin_subscriptions
|
|
|
119
119
|
def test_overlapping_ph_subscriptions() -> None:
|
|
120
120
|
sub = PeerSubscriptions()
|
|
121
121
|
|
|
122
|
--
assert sub.
|
|
123
|
--
assert sub.
|
|
122
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
123
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
124
124
|
|
|
125
125
|
assert sub.peers_for_puzzle_hash(ph1) == set()
|
|
126
126
|
assert sub.peers_for_puzzle_hash(ph2) == set()
|
|
127
127
|
|
|
128
128
|
# subscribed to different phs
|
|
129
|
--
ret = sub.
|
|
129
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1], 100)
|
|
130
130
|
assert ret == {ph1}
|
|
131
131
|
|
|
132
132
|
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
|
|
133
133
|
assert sub.peers_for_puzzle_hash(ph2) == set()
|
|
134
134
|
|
|
135
|
--
ret = sub.
|
|
135
|
++
ret = sub.add_puzzle_subscriptions(peer2, [ph2], 100)
|
|
136
136
|
assert ret == {ph2}
|
|
137
137
|
|
|
138
|
--
assert sub.
|
|
139
|
--
assert sub.
|
|
138
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
139
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
140
140
|
|
|
141
141
|
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
|
|
142
142
|
assert sub.peers_for_puzzle_hash(ph2) == {peer2}
|
|
143
143
|
|
|
144
144
|
# peer1 is now subscribing to both phs
|
|
145
|
--
ret = sub.
|
|
145
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph2], 100)
|
|
146
146
|
assert ret == {ph2}
|
|
147
147
|
|
|
148
|
--
assert sub.
|
|
149
|
--
assert sub.
|
|
148
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
149
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
150
150
|
|
|
151
151
|
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
|
|
152
152
|
assert sub.peers_for_puzzle_hash(ph2) == {peer1, peer2}
|
|
@@@ -154,8 -154,8 +154,8 @@@
|
|
|
154
154
|
# removing peer1 still leaves the subscription to ph2
|
|
155
155
|
sub.remove_peer(peer1)
|
|
156
156
|
|
|
157
|
--
assert sub.
|
|
158
|
--
assert sub.
|
|
157
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
158
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
159
159
|
|
|
160
160
|
assert sub.peers_for_puzzle_hash(ph1) == set()
|
|
161
161
|
assert sub.peers_for_puzzle_hash(ph2) == {peer2}
|
|
@@@ -164,19 -164,19 +164,19 @@@
|
|
|
164
164
|
def test_ph_sub_limit() -> None:
|
|
165
165
|
sub = PeerSubscriptions()
|
|
166
166
|
|
|
167
|
--
assert sub.
|
|
168
|
--
assert sub.
|
|
169
|
--
assert sub.
|
|
170
|
--
assert sub.
|
|
167
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
168
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
169
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
170
|
++
assert sub.has_puzzle_subscription(ph3) is False
|
|
171
171
|
|
|
172
|
--
ret = sub.
|
|
172
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3, ph4], 3)
|
|
173
173
|
# we only ended up subscribing to 3 puzzle hashes because of the limit
|
|
174
174
|
assert ret == {ph1, ph2, ph3}
|
|
175
175
|
|
|
176
|
--
assert sub.
|
|
177
|
--
assert sub.
|
|
178
|
--
assert sub.
|
|
179
|
--
assert sub.
|
|
176
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
177
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
178
|
++
assert sub.has_puzzle_subscription(ph3) is True
|
|
179
|
++
assert sub.has_puzzle_subscription(ph4) is False
|
|
180
180
|
|
|
181
181
|
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
|
|
182
182
|
assert sub.peers_for_puzzle_hash(ph2) == {peer1}
|
|
@@@ -184,10 -184,10 +184,10 @@@
|
|
|
184
184
|
assert sub.peers_for_puzzle_hash(ph4) == set()
|
|
185
185
|
|
|
186
186
|
# peer1 should still be limited
|
|
187
|
--
ret = sub.
|
|
187
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph4], 3)
|
|
188
188
|
assert ret == set()
|
|
189
189
|
|
|
190
|
--
assert sub.
|
|
190
|
++
assert sub.has_puzzle_subscription(ph4) is False
|
|
191
191
|
assert sub.peers_for_puzzle_hash(ph4) == set()
|
|
192
192
|
|
|
193
193
|
# peer1 is also limied on coin subscriptions
|
|
@@@ -197,10 -197,10 +197,10 @@@
|
|
|
197
197
|
assert sub.peers_for_coin_id(coin1) == set()
|
|
198
198
|
|
|
199
199
|
# peer2 is has its own limit
|
|
200
|
--
ret = sub.
|
|
200
|
++
ret = sub.add_puzzle_subscriptions(peer2, [ph4], 3)
|
|
201
201
|
assert ret == {ph4}
|
|
202
202
|
|
|
203
|
--
assert sub.
|
|
203
|
++
assert sub.has_puzzle_subscription(ph4) is True
|
|
204
204
|
assert sub.peers_for_puzzle_hash(ph4) == {peer2}
|
|
205
205
|
|
|
206
206
|
sub.remove_peer(peer1)
|
|
@@@ -210,18 -210,18 +210,18 @@@
|
|
|
210
210
|
def test_ph_sub_limit_incremental() -> None:
|
|
211
211
|
sub = PeerSubscriptions()
|
|
212
212
|
|
|
213
|
--
assert sub.
|
|
214
|
--
assert sub.
|
|
215
|
--
assert sub.
|
|
216
|
--
assert sub.
|
|
213
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
214
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
215
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
216
|
++
assert sub.has_puzzle_subscription(ph3) is False
|
|
217
217
|
|
|
218
|
--
ret = sub.
|
|
218
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1], 2)
|
|
219
219
|
assert ret == {ph1}
|
|
220
220
|
|
|
221
|
--
assert sub.
|
|
222
|
--
assert sub.
|
|
223
|
--
assert sub.
|
|
224
|
--
assert sub.
|
|
221
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
222
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
223
|
++
assert sub.has_puzzle_subscription(ph3) is False
|
|
224
|
++
assert sub.has_puzzle_subscription(ph4) is False
|
|
225
225
|
|
|
226
226
|
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
|
|
227
227
|
assert sub.peers_for_puzzle_hash(ph2) == set()
|
|
@@@ -229,13 -229,13 +229,13 @@@
|
|
|
229
229
|
assert sub.peers_for_puzzle_hash(ph4) == set()
|
|
230
230
|
|
|
231
231
|
# this will cross the limit. Only ph2 will be added
|
|
232
|
--
ret = sub.
|
|
232
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph2, ph3], 2)
|
|
233
233
|
assert ret == {ph2}
|
|
234
234
|
|
|
235
|
--
assert sub.
|
|
236
|
--
assert sub.
|
|
237
|
--
assert sub.
|
|
238
|
--
assert sub.
|
|
235
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
236
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
237
|
++
assert sub.has_puzzle_subscription(ph3) is False
|
|
238
|
++
assert sub.has_puzzle_subscription(ph4) is False
|
|
239
239
|
|
|
240
240
|
assert sub.peers_for_puzzle_hash(ph1) == {peer1}
|
|
241
241
|
assert sub.peers_for_puzzle_hash(ph2) == {peer1}
|
|
@@@ -272,10 -272,10 +272,10 @@@ def test_coin_sub_limit() -> None
|
|
|
272
272
|
assert sub.peers_for_coin_id(coin4) == set()
|
|
273
273
|
|
|
274
274
|
# peer1 is also limied on ph subscriptions
|
|
275
|
--
ret = sub.
|
|
275
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1], 3)
|
|
276
276
|
assert ret == set()
|
|
277
277
|
|
|
278
|
--
assert sub.
|
|
278
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
279
279
|
assert sub.peers_for_puzzle_hash(ph1) == set()
|
|
280
280
|
|
|
281
281
|
# peer2 is has its own limit
|
|
@@@ -327,31 -327,31 +327,77 @@@ def test_coin_sub_limit_incremental() -
|
|
|
327
327
|
def test_ph_subscription_duplicates() -> None:
|
|
328
328
|
sub = PeerSubscriptions()
|
|
329
329
|
|
|
330
|
--
assert sub.
|
|
331
|
--
assert sub.
|
|
332
|
--
assert sub.
|
|
333
|
--
assert sub.
|
|
330
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
331
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
332
|
++
assert sub.has_puzzle_subscription(ph3) is False
|
|
333
|
++
assert sub.has_puzzle_subscription(ph4) is False
|
|
334
334
|
|
|
335
|
--
ret = sub.
|
|
335
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3], 100)
|
|
336
336
|
assert ret == {ph1, ph2, ph3}
|
|
337
337
|
|
|
338
|
--
assert sub.
|
|
339
|
--
assert sub.
|
|
340
|
--
assert sub.
|
|
341
|
--
assert sub.
|
|
338
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
339
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
340
|
++
assert sub.has_puzzle_subscription(ph3) is True
|
|
341
|
++
assert sub.has_puzzle_subscription(ph4) is False
|
|
342
342
|
|
|
343
343
|
# only ph4 is new, the others are duplicates and ignored
|
|
344
|
--
ret = sub.
|
|
344
|
++
ret = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3, ph4], 100)
|
|
345
345
|
assert ret == {ph4}
|
|
346
346
|
|
|
347
|
--
assert sub.
|
|
348
|
--
assert sub.
|
|
349
|
--
assert sub.
|
|
350
|
--
assert sub.
|
|
347
|
++
assert sub.has_puzzle_subscription(ph1) is True
|
|
348
|
++
assert sub.has_puzzle_subscription(ph2) is True
|
|
349
|
++
assert sub.has_puzzle_subscription(ph3) is True
|
|
350
|
++
assert sub.has_puzzle_subscription(ph4) is True
|
|
351
351
|
|
|
352
352
|
sub.remove_peer(peer1)
|
|
353
353
|
|
|
354
|
--
assert sub.
|
|
355
|
--
assert sub.
|
|
356
|
--
assert sub.
|
|
357
|
--
assert sub.
|
|
354
|
++
assert sub.has_puzzle_subscription(ph1) is False
|
|
355
|
++
assert sub.has_puzzle_subscription(ph2) is False
|
|
356
|
++
assert sub.has_puzzle_subscription(ph3) is False
|
|
357
|
++
assert sub.has_puzzle_subscription(ph4) is False
|
|
358
|
++
|
|
359
|
++
|
|
360
|
++
def test_remove_ph_subscriptions() -> None:
|
|
361
|
++
sub = PeerSubscriptions()
|
|
362
|
++
|
|
363
|
++
added = sub.add_puzzle_subscriptions(peer1, [ph1, ph2, ph3, ph4, ph4], 100)
|
|
364
|
++
assert added == {ph1, ph2, ph3, ph4}
|
|
365
|
++
|
|
366
|
++
removed = sub.remove_puzzle_subscriptions(peer1, list(added))
|
|
367
|
++
assert removed == added
|
|
368
|
++
|
|
369
|
++
# These have already been removed.
|
|
370
|
++
assert len(sub.remove_puzzle_subscriptions(peer1, [ph1, ph2])) == 0
|
|
371
|
++
|
|
372
|
++
assert sub.peer_subscription_count(peer1) == 0
|
|
373
|
++
|
|
374
|
++
for ph in removed:
|
|
375
|
++
assert not sub.has_puzzle_subscription(ph)
|
|
376
|
++
|
|
377
|
++
|
|
378
|
++
def test_remove_coin_subscriptions() -> None:
|
|
379
|
++
sub = PeerSubscriptions()
|
|
380
|
++
|
|
381
|
++
added = sub.add_coin_subscriptions(peer1, [coin1, coin2, coin3, coin4, coin4], 100)
|
|
382
|
++
assert added == {coin1, coin2, coin3, coin4}
|
|
383
|
++
|
|
384
|
++
removed = sub.remove_coin_subscriptions(peer1, list(added))
|
|
385
|
++
assert removed == added
|
|
386
|
++
|
|
387
|
++
# These have already been removed.
|
|
388
|
++
assert len(sub.remove_coin_subscriptions(peer1, [coin1, coin2])) == 0
|
|
389
|
++
|
|
390
|
++
assert sub.peer_subscription_count(peer1) == 0
|
|
391
|
++
|
|
392
|
++
for coin_id in removed:
|
|
393
|
++
assert not sub.has_coin_subscription(coin_id)
|
|
394
|
++
|
|
395
|
++
|
|
396
|
++
def test_subscription_list() -> None:
|
|
397
|
++
sub = PeerSubscriptions()
|
|
398
|
++
|
|
399
|
++
sub.add_coin_subscriptions(peer1, [coin1, coin2], 4)
|
|
400
|
++
sub.add_puzzle_subscriptions(peer1, [ph1, ph2], 4)
|
|
401
|
++
|
|
402
|
++
assert sub.coin_subscriptions(peer1) == {coin1, coin2}
|
|
403
|
++
assert sub.puzzle_subscriptions(peer1) == {ph1, ph2}
|
|
@@@ -7,19 -7,19 +7,14 @@@ import pytes
|
|
|
7
7
|
from chia.types.full_block import FullBlock
|
|
8
8
|
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
|
9
9
|
from chia.types.peer_info import PeerInfo
|
|
10
|
--
from chia.util.ints import
|
|
10
|
++
from chia.util.ints import uint64, uint128
|
|
11
11
|
from chia.wallet.util.tx_config import DEFAULT_TX_CONFIG
|
|
12
12
|
from chia.wallet.wallet_node import WalletNode
|
|
13
|
--
from tests.util.misc import BenchmarkRunner
|
|
13
|
++
from tests.util.misc import BenchmarkRunner, wallet_height_at_least
|
|
14
14
|
from tests.util.setup_nodes import OldSimulatorsAndWallets
|
|
15
15
|
from tests.util.time_out_assert import time_out_assert
|
|
16
16
|
|
|
17
17
|
|
|
18
|
--
async def wallet_height_at_least(wallet_node: WalletNode, h: uint32) -> bool:
|
|
19
|
--
height = await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()
|
|
20
|
--
return height == h
|
|
21
|
--
|
|
22
|
--
|
|
23
18
|
async def wallet_balance_at_least(wallet_node: WalletNode, balance: uint128) -> bool:
|
|
24
19
|
b = await wallet_node.wallet_state_manager.get_confirmed_balance_for_wallet(1)
|
|
25
20
|
return b >= balance
|
|
@@@ -14,6 -14,6 +14,7 @@@ from typing import AsyncIterator, List
|
|
|
14
14
|
import anyio
|
|
15
15
|
import pytest
|
|
16
16
|
|
|
17
|
++
import tests
|
|
17
18
|
from chia.server import chia_policy
|
|
18
19
|
from chia.util.timing import adjusted_timeout
|
|
19
20
|
from tests.core.server import serve
|
|
@@@ -160,9 -160,9 +161,12 @@@ async def test_loop(tmp_path: pathlib.P
|
|
|
160
161
|
flood_file = tmp_path.joinpath("flood")
|
|
161
162
|
flood_file.touch()
|
|
162
163
|
|
|
164
|
++
env = {**os.environ, "PYTHONPATH": pathlib.Path(tests.__file__).parent.parent.as_posix()}
|
|
165
|
++
|
|
163
166
|
logger.info(" ==== launching serve.py")
|
|
164
167
|
with subprocess.Popen(
|
|
165
168
|
[sys.executable, "-m", "tests.core.server.serve", os.fspath(serve_file)],
|
|
169
|
++
env=env,
|
|
166
170
|
):
|
|
167
171
|
logger.info(" ==== serve.py running")
|
|
168
172
|
|
|
@@@ -171,6 -171,6 +175,7 @@@
|
|
|
171
175
|
logger.info(" ==== launching flood.py")
|
|
172
176
|
with subprocess.Popen(
|
|
173
177
|
[sys.executable, "-m", "tests.core.server.flood", os.fspath(flood_file)],
|
|
178
|
++
env=env,
|
|
174
179
|
):
|
|
175
180
|
logger.info(" ==== flood.py running")
|
|
176
181
|
|
|
@@@ -2,10 -2,10 +2,10 @@@ from __future__ import annotation
|
|
|
2
2
|
|
|
3
3
|
from itertools import permutations
|
|
4
4
|
|
|
5
|
--
from benchmarks.utils import rand_hash
|
|
6
5
|
from chia.types.blockchain_format.coin import hash_coin_ids
|
|
7
6
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
8
7
|
from chia.util.hash import std_hash
|
|
8
|
++
from tests.util.benchmarks import rand_hash
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def test_hash_coin_ids_empty() -> None:
|
|
@@@ -631,6 -631,6 +631,10 @@@ async def test_get_blockchain_state(one
|
|
|
631
631
|
# When supplying genesis block, there are no older blocks so `None` should be returned
|
|
632
632
|
assert await get_average_block_time(full_node_api_1.full_node.blockchain, block_records[0], 4608) is None
|
|
633
633
|
assert await get_average_block_time(full_node_api_1.full_node.blockchain, block_records[-1], 4608) is not None
|
|
634
|
++
# Test that get_aggsig_additional_data() returns correctly
|
|
635
|
++
assert (
|
|
636
|
++
full_node_api_1.full_node.constants.AGG_SIG_ME_ADDITIONAL_DATA == await client.get_aggsig_additional_data()
|
|
637
|
++
)
|
|
634
638
|
|
|
635
639
|
finally:
|
|
636
640
|
# Checks that the RPC manages to stop the node
|
|
@@@ -1,15 -1,15 +1,16 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
--
import pathlib
|
|
5
4
|
import random
|
|
6
5
|
from dataclasses import replace
|
|
7
6
|
from typing import Callable, List, Optional, Tuple
|
|
8
7
|
|
|
8
|
++
import pkg_resources
|
|
9
9
|
import pytest
|
|
10
10
|
from chia_rs import AugSchemeMPL, G1Element, PrivateKey
|
|
11
11
|
|
|
12
12
|
import tests
|
|
13
|
++
import tests.util
|
|
13
14
|
from chia.simulator.keyring import TempKeyring
|
|
14
15
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
15
16
|
from chia.util.errors import (
|
|
@@@ -156,7 -156,7 +157,8 @@@ class TestKeychain
|
|
|
156
157
|
assert child_sk == PrivateKey.from_bytes(tv_child_int.to_bytes(32, "big"))
|
|
157
158
|
|
|
158
159
|
def test_bip39_test_vectors(self):
|
|
159
|
--
|
|
160
|
++
test_vectors_path = pkg_resources.resource_filename(tests.util.__name__, "bip39_test_vectors.json")
|
|
161
|
++
with open(test_vectors_path) as f:
|
|
160
162
|
all_vectors = json.loads(f.read())
|
|
161
163
|
|
|
162
164
|
for vector_list in all_vectors["english"]:
|
|
@@@ -172,7 -172,7 +174,7 @@@
|
|
|
172
174
|
"""
|
|
173
175
|
Tests that the first 4 letters of each mnemonic phrase matches as if it were the full phrase
|
|
174
176
|
"""
|
|
175
|
--
test_vectors_path =
|
|
177
|
++
test_vectors_path = pkg_resources.resource_filename(tests.util.__name__, "bip39_test_vectors.json")
|
|
176
178
|
with open(test_vectors_path) as f:
|
|
177
179
|
all_vectors = json.load(f)
|
|
178
180
|
|
|
@@@ -555,7 -555,7 +555,7 @@@ def test_recursive_types() -> None
|
|
|
555
555
|
|
|
556
556
|
|
|
557
557
|
def test_ambiguous_deserialization_optionals() -> None:
|
|
558
|
--
with pytest.raises(
|
|
558
|
++
with pytest.raises(ValueError, match="unexpected end of buffer"):
|
|
559
559
|
SubEpochChallengeSegment.from_bytes(b"\x00\x00\x00\x03\xff\xff\xff\xff")
|
|
560
560
|
|
|
561
561
|
@streamable
|
|
@@@ -8,9 -8,9 +8,9 @@@ from unittest.mock import MagicMoc
|
|
|
8
8
|
import pytest
|
|
9
9
|
from chia_rs import G1Element
|
|
10
10
|
|
|
11
|
--
from benchmarks.utils import rand_g1, rand_hash
|
|
12
11
|
from chia.pools.pool_wallet import PoolWallet
|
|
13
12
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
13
|
++
from tests.util.benchmarks import rand_g1, rand_hash
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@dataclass
|
|
@@@ -8,7 -8,7 +8,7 @@@ from pathlib import Pat
|
|
|
8
8
|
|
|
9
9
|
import pytest
|
|
10
10
|
|
|
11
|
--
from
|
|
11
|
++
from tests.util.full_sync import run_sync_test
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@pytest.mark.parametrize("keep_up", [True, False])
|
|
@@@ -12,7 -12,7 +12,7 @@@ except ImportError
|
|
|
12
12
|
if sys.platform == "linux":
|
|
13
13
|
raise
|
|
14
14
|
|
|
15
|
--
from
|
|
15
|
++
from chia.legacy.keyring import create_legacy_keyring, generate_and_add, get_keys, legacy_keyring
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def show() -> Result:
|
|
@@@ -10,7 -10,7 +10,7 @@@ from chia.types.blockchain_format.sized
|
|
|
10
10
|
from chia.types.condition_opcodes import ConditionOpcode
|
|
11
11
|
from chia.types.condition_with_args import ConditionWithArgs
|
|
12
12
|
from chia.util.ints import uint32, uint64, uint128
|
|
13
|
--
from
|
|
13
|
++
from tests.util.run_block import run_json_block
|
|
14
14
|
|
|
15
15
|
constants = dataclasses.replace(
|
|
16
16
|
DEFAULT_CONSTANTS,
|
|
@@@ -42,7 -42,7 +42,7 @@@ def run_and_return_cost_time(chialisp)
|
|
|
42
42
|
clvm_loop_solution = f"(1000 {chialisp})"
|
|
43
43
|
solution_program = Program.to(binutils.assemble(clvm_loop_solution))
|
|
44
44
|
|
|
45
|
--
cost,
|
|
45
|
++
cost, _ = loop_program.run_with_cost(INFINITE_COST, solution_program)
|
|
46
46
|
|
|
47
47
|
end = time.time()
|
|
48
48
|
total_time = end - start
|
|
@@@ -135,7 -135,7 +135,7 @@@ if __name__ == "__main__"
|
|
|
135
135
|
puzzle_start = time.time()
|
|
136
136
|
clvm_cost = 0
|
|
137
137
|
for i in range(0, 1000):
|
|
138
|
--
cost_run,
|
|
138
|
++
cost_run, _ = puzzles[i].run_with_cost(INFINITE_COST, solutions[i])
|
|
139
139
|
clvm_cost += cost_run
|
|
140
140
|
|
|
141
141
|
puzzle_end = time.time()
|
|
@@@ -1,0 -1,0 +1,155 @@@
|
|
|
1
|
++
from __future__ import annotations
|
|
2
|
++
|
|
3
|
++
import random
|
|
4
|
++
from typing import Tuple
|
|
5
|
++
|
|
6
|
++
import pkg_resources
|
|
7
|
++
from chia_rs import AugSchemeMPL, ClassgroupElement, Coin, G1Element, G2Element, VDFInfo, VDFProof
|
|
8
|
++
|
|
9
|
++
from chia.consensus.coinbase import create_farmer_coin, create_pool_coin
|
|
10
|
++
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
11
|
++
from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
|
|
12
|
++
from chia.types.blockchain_format.pool_target import PoolTarget
|
|
13
|
++
from chia.types.blockchain_format.proof_of_space import ProofOfSpace
|
|
14
|
++
from chia.types.blockchain_format.reward_chain_block import RewardChainBlock
|
|
15
|
++
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
16
|
++
from chia.types.blockchain_format.sized_bytes import bytes32, bytes100
|
|
17
|
++
from chia.types.full_block import FullBlock
|
|
18
|
++
from chia.util.ints import uint8, uint32, uint64, uint128
|
|
19
|
++
|
|
20
|
++
# farmer puzzle hash
|
|
21
|
++
ph = bytes32(b"a" * 32)
|
|
22
|
++
|
|
23
|
++
clvm_generator_bin_path = pkg_resources.resource_filename(__name__, "clvm_generator.bin")
|
|
24
|
++
with open(clvm_generator_bin_path, "rb") as f:
|
|
25
|
++
clvm_generator = f.read()
|
|
26
|
++
|
|
27
|
++
|
|
28
|
++
def rewards(height: uint32) -> Tuple[Coin, Coin]:
|
|
29
|
++
farmer_coin = create_farmer_coin(height, ph, uint64(250000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
|
|
30
|
++
pool_coin = create_pool_coin(height, ph, uint64(1750000000), DEFAULT_CONSTANTS.GENESIS_CHALLENGE)
|
|
31
|
++
return farmer_coin, pool_coin
|
|
32
|
++
|
|
33
|
++
|
|
34
|
++
def rand_bytes(num: int) -> bytes:
|
|
35
|
++
ret = bytearray(num)
|
|
36
|
++
for i in range(num):
|
|
37
|
++
ret[i] = random.getrandbits(8)
|
|
38
|
++
return bytes(ret)
|
|
39
|
++
|
|
40
|
++
|
|
41
|
++
def rand_hash() -> bytes32:
|
|
42
|
++
return bytes32(rand_bytes(32))
|
|
43
|
++
|
|
44
|
++
|
|
45
|
++
def rand_g1() -> G1Element:
|
|
46
|
++
sk = AugSchemeMPL.key_gen(rand_bytes(96))
|
|
47
|
++
return sk.get_g1()
|
|
48
|
++
|
|
49
|
++
|
|
50
|
++
def rand_g2() -> G2Element:
|
|
51
|
++
sk = AugSchemeMPL.key_gen(rand_bytes(96))
|
|
52
|
++
return AugSchemeMPL.sign(sk, b"foobar")
|
|
53
|
++
|
|
54
|
++
|
|
55
|
++
def rand_class_group_element() -> ClassgroupElement:
|
|
56
|
++
return ClassgroupElement(bytes100(rand_bytes(100)))
|
|
57
|
++
|
|
58
|
++
|
|
59
|
++
def rand_vdf() -> VDFInfo:
|
|
60
|
++
return VDFInfo(rand_hash(), uint64(random.randint(100000, 1000000000)), rand_class_group_element())
|
|
61
|
++
|
|
62
|
++
|
|
63
|
++
def rand_vdf_proof() -> VDFProof:
|
|
64
|
++
return VDFProof(
|
|
65
|
++
uint8(1), # witness_type
|
|
66
|
++
rand_hash(), # witness
|
|
67
|
++
bool(random.randint(0, 1)), # normalized_to_identity
|
|
68
|
++
)
|
|
69
|
++
|
|
70
|
++
|
|
71
|
++
def rand_full_block() -> FullBlock:
|
|
72
|
++
proof_of_space = ProofOfSpace(
|
|
73
|
++
rand_hash(),
|
|
74
|
++
rand_g1(),
|
|
75
|
++
None,
|
|
76
|
++
rand_g1(),
|
|
77
|
++
uint8(0),
|
|
78
|
++
rand_bytes(8 * 32),
|
|
79
|
++
)
|
|
80
|
++
|
|
81
|
++
reward_chain_block = RewardChainBlock(
|
|
82
|
++
uint128(1),
|
|
83
|
++
uint32(2),
|
|
84
|
++
uint128(3),
|
|
85
|
++
uint8(4),
|
|
86
|
++
rand_hash(),
|
|
87
|
++
proof_of_space,
|
|
88
|
++
None,
|
|
89
|
++
rand_g2(),
|
|
90
|
++
rand_vdf(),
|
|
91
|
++
None,
|
|
92
|
++
rand_g2(),
|
|
93
|
++
rand_vdf(),
|
|
94
|
++
rand_vdf(),
|
|
95
|
++
True,
|
|
96
|
++
)
|
|
97
|
++
|
|
98
|
++
pool_target = PoolTarget(
|
|
99
|
++
rand_hash(),
|
|
100
|
++
uint32(0),
|
|
101
|
++
)
|
|
102
|
++
|
|
103
|
++
foliage_block_data = FoliageBlockData(
|
|
104
|
++
rand_hash(),
|
|
105
|
++
pool_target,
|
|
106
|
++
rand_g2(),
|
|
107
|
++
rand_hash(),
|
|
108
|
++
rand_hash(),
|
|
109
|
++
)
|
|
110
|
++
|
|
111
|
++
foliage = Foliage(
|
|
112
|
++
rand_hash(),
|
|
113
|
++
rand_hash(),
|
|
114
|
++
foliage_block_data,
|
|
115
|
++
rand_g2(),
|
|
116
|
++
rand_hash(),
|
|
117
|
++
rand_g2(),
|
|
118
|
++
)
|
|
119
|
++
|
|
120
|
++
foliage_transaction_block = FoliageTransactionBlock(
|
|
121
|
++
rand_hash(),
|
|
122
|
++
uint64(0),
|
|
123
|
++
rand_hash(),
|
|
124
|
++
rand_hash(),
|
|
125
|
++
rand_hash(),
|
|
126
|
++
rand_hash(),
|
|
127
|
++
)
|
|
128
|
++
|
|
129
|
++
farmer_coin, pool_coin = rewards(uint32(0))
|
|
130
|
++
|
|
131
|
++
transactions_info = TransactionsInfo(
|
|
132
|
++
rand_hash(),
|
|
133
|
++
rand_hash(),
|
|
134
|
++
rand_g2(),
|
|
135
|
++
uint64(0),
|
|
136
|
++
uint64(1),
|
|
137
|
++
[farmer_coin, pool_coin],
|
|
138
|
++
)
|
|
139
|
++
|
|
140
|
++
full_block = FullBlock(
|
|
141
|
++
[],
|
|
142
|
++
reward_chain_block,
|
|
143
|
++
rand_vdf_proof(),
|
|
144
|
++
rand_vdf_proof(),
|
|
145
|
++
rand_vdf_proof(),
|
|
146
|
++
rand_vdf_proof(),
|
|
147
|
++
rand_vdf_proof(),
|
|
148
|
++
foliage,
|
|
149
|
++
foliage_transaction_block,
|
|
150
|
++
transactions_info,
|
|
151
|
++
SerializedProgram.from_bytes(clvm_generator),
|
|
152
|
++
[],
|
|
153
|
++
)
|
|
154
|
++
|
|
155
|
++
return full_block
|
|
@@@ -1,0 -1,0 +1,226 @@@
|
|
|
1
|
++
from __future__ import annotations
|
|
2
|
++
|
|
3
|
++
import cProfile
|
|
4
|
++
import logging
|
|
5
|
++
import shutil
|
|
6
|
++
import tempfile
|
|
7
|
++
import time
|
|
8
|
++
from contextlib import contextmanager
|
|
9
|
++
from pathlib import Path
|
|
10
|
++
from typing import Iterator, List, Optional, cast
|
|
11
|
++
|
|
12
|
++
import aiosqlite
|
|
13
|
++
import zstd
|
|
14
|
++
|
|
15
|
++
from chia.cmds.init_funcs import chia_init
|
|
16
|
++
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
17
|
++
from chia.full_node.full_node import FullNode
|
|
18
|
++
from chia.server.outbound_message import Message, NodeType
|
|
19
|
++
from chia.server.server import ChiaServer
|
|
20
|
++
from chia.server.ws_connection import ConnectionCallback, WSChiaConnection
|
|
21
|
++
from chia.simulator.block_tools import make_unfinished_block
|
|
22
|
++
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
23
|
++
from chia.types.full_block import FullBlock
|
|
24
|
++
from chia.types.peer_info import PeerInfo
|
|
25
|
++
from chia.util.config import load_config
|
|
26
|
++
from chia.util.ints import uint16
|
|
27
|
++
from tests.util.constants import test_constants as TEST_CONSTANTS
|
|
28
|
++
|
|
29
|
++
|
|
30
|
++
class ExitOnError(logging.Handler):
|
|
31
|
++
def __init__(self) -> None:
|
|
32
|
++
super().__init__()
|
|
33
|
++
self.exit_with_failure = False
|
|
34
|
++
|
|
35
|
++
def emit(self, record: logging.LogRecord) -> None:
|
|
36
|
++
if record.levelno != logging.ERROR:
|
|
37
|
++
return
|
|
38
|
++
self.exit_with_failure = True
|
|
39
|
++
|
|
40
|
++
|
|
41
|
++
@contextmanager
|
|
42
|
++
def enable_profiler(profile: bool, counter: int) -> Iterator[None]:
|
|
43
|
++
if not profile:
|
|
44
|
++
yield
|
|
45
|
++
return
|
|
46
|
++
|
|
47
|
++
with cProfile.Profile() as pr:
|
|
48
|
++
receive_start_time = time.monotonic()
|
|
49
|
++
yield
|
|
50
|
++
|
|
51
|
++
if time.monotonic() - receive_start_time > 5:
|
|
52
|
++
pr.create_stats()
|
|
53
|
++
pr.dump_stats(f"slow-batch-{counter:05d}.profile")
|
|
54
|
++
|
|
55
|
++
|
|
56
|
++
class FakeServer:
|
|
57
|
++
async def send_to_all(
|
|
58
|
++
self, messages: List[Message], node_type: NodeType, exclude: Optional[bytes32] = None
|
|
59
|
++
) -> None:
|
|
60
|
++
pass
|
|
61
|
++
|
|
62
|
++
def set_received_message_callback(self, callback: ConnectionCallback) -> None:
|
|
63
|
++
pass
|
|
64
|
++
|
|
65
|
++
async def get_peer_info(self) -> Optional[PeerInfo]:
|
|
66
|
++
return None
|
|
67
|
++
|
|
68
|
++
def get_connections(
|
|
69
|
++
self, node_type: Optional[NodeType] = None, *, outbound: Optional[bool] = False
|
|
70
|
++
) -> List[WSChiaConnection]:
|
|
71
|
++
return []
|
|
72
|
++
|
|
73
|
++
def is_duplicate_or_self_connection(self, target_node: PeerInfo) -> bool:
|
|
74
|
++
return False
|
|
75
|
++
|
|
76
|
++
async def start_client(
|
|
77
|
++
self,
|
|
78
|
++
target_node: PeerInfo,
|
|
79
|
++
on_connect: Optional[ConnectionCallback] = None,
|
|
80
|
++
auth: bool = False,
|
|
81
|
++
is_feeler: bool = False,
|
|
82
|
++
) -> bool:
|
|
83
|
++
return False
|
|
84
|
++
|
|
85
|
++
|
|
86
|
++
class FakePeer:
|
|
87
|
++
def get_peer_logging(self) -> PeerInfo:
|
|
88
|
++
return PeerInfo("0.0.0.0", uint16(0))
|
|
89
|
++
|
|
90
|
++
def __init__(self) -> None:
|
|
91
|
++
self.peer_node_id = bytes([0] * 32)
|
|
92
|
++
|
|
93
|
++
async def get_peer_info(self) -> Optional[PeerInfo]:
|
|
94
|
++
return None
|
|
95
|
++
|
|
96
|
++
|
|
97
|
++
async def run_sync_test(
|
|
98
|
++
file: Path,
|
|
99
|
++
db_version: int,
|
|
100
|
++
profile: bool,
|
|
101
|
++
single_thread: bool,
|
|
102
|
++
test_constants: bool,
|
|
103
|
++
keep_up: bool,
|
|
104
|
++
db_sync: str,
|
|
105
|
++
node_profiler: bool,
|
|
106
|
++
start_at_checkpoint: Optional[str],
|
|
107
|
++
) -> None:
|
|
108
|
++
logger = logging.getLogger()
|
|
109
|
++
logger.setLevel(logging.WARNING)
|
|
110
|
++
handler = logging.FileHandler("test-full-sync.log")
|
|
111
|
++
handler.setFormatter(
|
|
112
|
++
logging.Formatter(
|
|
113
|
++
"%(levelname)-8s %(message)s",
|
|
114
|
++
datefmt="%Y-%m-%dT%H:%M:%S",
|
|
115
|
++
)
|
|
116
|
++
)
|
|
117
|
++
logger.addHandler(handler)
|
|
118
|
++
check_log = ExitOnError()
|
|
119
|
++
logger.addHandler(check_log)
|
|
120
|
++
|
|
121
|
++
with tempfile.TemporaryDirectory() as root_dir:
|
|
122
|
++
root_path = Path(root_dir, "root")
|
|
123
|
++
if start_at_checkpoint is not None:
|
|
124
|
++
shutil.copytree(start_at_checkpoint, root_path)
|
|
125
|
++
|
|
126
|
++
chia_init(root_path, should_check_keys=False, v1_db=(db_version == 1))
|
|
127
|
++
config = load_config(root_path, "config.yaml")
|
|
128
|
++
|
|
129
|
++
if test_constants:
|
|
130
|
++
constants = TEST_CONSTANTS
|
|
131
|
++
else:
|
|
132
|
++
overrides = config["network_overrides"]["constants"][config["selected_network"]]
|
|
133
|
++
constants = DEFAULT_CONSTANTS.replace_str_to_bytes(**overrides)
|
|
134
|
++
if single_thread:
|
|
135
|
++
config["full_node"]["single_threaded"] = True
|
|
136
|
++
config["full_node"]["db_sync"] = db_sync
|
|
137
|
++
config["full_node"]["enable_profiler"] = node_profiler
|
|
138
|
++
full_node = await FullNode.create(
|
|
139
|
++
config["full_node"],
|
|
140
|
++
root_path=root_path,
|
|
141
|
++
consensus_constants=constants,
|
|
142
|
++
)
|
|
143
|
++
|
|
144
|
++
full_node.set_server(cast(ChiaServer, FakeServer()))
|
|
145
|
++
async with full_node.manage():
|
|
146
|
++
peak = full_node.blockchain.get_peak()
|
|
147
|
++
if peak is not None:
|
|
148
|
++
height = int(peak.height)
|
|
149
|
++
else:
|
|
150
|
++
height = 0
|
|
151
|
++
|
|
152
|
++
peer: WSChiaConnection = cast(WSChiaConnection, FakePeer())
|
|
153
|
++
|
|
154
|
++
print()
|
|
155
|
++
counter = 0
|
|
156
|
++
monotonic = height
|
|
157
|
++
prev_hash = None
|
|
158
|
++
async with aiosqlite.connect(file) as in_db:
|
|
159
|
++
await in_db.execute("pragma query_only")
|
|
160
|
++
rows = await in_db.execute(
|
|
161
|
++
"SELECT header_hash, height, block FROM full_blocks "
|
|
162
|
++
"WHERE height >= ? AND in_main_chain=1 ORDER BY height",
|
|
163
|
++
(height,),
|
|
164
|
++
)
|
|
165
|
++
|
|
166
|
++
block_batch = []
|
|
167
|
++
|
|
168
|
++
start_time = time.monotonic()
|
|
169
|
++
logger.warning(f"starting test {start_time}")
|
|
170
|
++
worst_batch_height = None
|
|
171
|
++
worst_batch_time_per_block = None
|
|
172
|
++
peer_info = peer.get_peer_logging()
|
|
173
|
++
async for r in rows:
|
|
174
|
++
batch_start_time = time.monotonic()
|
|
175
|
++
with enable_profiler(profile, height):
|
|
176
|
++
block = FullBlock.from_bytes(zstd.decompress(r[2]))
|
|
177
|
++
block_batch.append(block)
|
|
178
|
++
|
|
179
|
++
assert block.height == monotonic
|
|
180
|
++
monotonic += 1
|
|
181
|
++
assert prev_hash is None or block.prev_header_hash == prev_hash
|
|
182
|
++
prev_hash = block.header_hash
|
|
183
|
++
|
|
184
|
++
if len(block_batch) < 32:
|
|
185
|
++
continue
|
|
186
|
++
|
|
187
|
++
if keep_up:
|
|
188
|
++
for b in block_batch:
|
|
189
|
++
await full_node.add_unfinished_block(make_unfinished_block(b, constants), peer)
|
|
190
|
++
await full_node.add_block(b)
|
|
191
|
++
else:
|
|
192
|
++
success, summary, _ = await full_node.add_block_batch(block_batch, peer_info, None)
|
|
193
|
++
end_height = block_batch[-1].height
|
|
194
|
++
full_node.blockchain.clean_block_record(end_height - full_node.constants.BLOCKS_CACHE_SIZE)
|
|
195
|
++
|
|
196
|
++
if not success:
|
|
197
|
++
raise RuntimeError("failed to ingest block batch")
|
|
198
|
++
|
|
199
|
++
assert summary is not None
|
|
200
|
++
|
|
201
|
++
time_per_block = (time.monotonic() - batch_start_time) / len(block_batch)
|
|
202
|
++
if not worst_batch_height or worst_batch_time_per_block > time_per_block:
|
|
203
|
++
worst_batch_height = height
|
|
204
|
++
worst_batch_time_per_block = time_per_block
|
|
205
|
++
|
|
206
|
++
counter += len(block_batch)
|
|
207
|
++
height += len(block_batch)
|
|
208
|
++
print(
|
|
209
|
++
f"\rheight {height} {time_per_block:0.2f} s/block ",
|
|
210
|
++
end="",
|
|
211
|
++
)
|
|
212
|
++
block_batch = []
|
|
213
|
++
if check_log.exit_with_failure:
|
|
214
|
++
raise RuntimeError("error printed to log. exiting")
|
|
215
|
++
|
|
216
|
++
if counter >= 100000:
|
|
217
|
++
counter = 0
|
|
218
|
++
print()
|
|
219
|
++
end_time = time.monotonic()
|
|
220
|
++
logger.warning(f"test completed at {end_time}")
|
|
221
|
++
logger.warning(f"duration: {end_time - start_time:0.2f} s")
|
|
222
|
++
logger.warning(f"worst time-per-block: {worst_batch_time_per_block:0.2f} s")
|
|
223
|
++
logger.warning(f"worst height: {worst_batch_height}")
|
|
224
|
++
logger.warning(f"end-height: {height}")
|
|
225
|
++
if node_profiler:
|
|
226
|
++
(root_path / "profile-node").rename("./profile-node")
|
|
@@@ -27,8 -27,8 +27,9 @@@ from chia.full_node.mempool import Memp
|
|
|
27
27
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
28
28
|
from chia.types.condition_opcodes import ConditionOpcode
|
|
29
29
|
from chia.util.hash import std_hash
|
|
30
|
--
from chia.util.ints import uint64
|
|
30
|
++
from chia.util.ints import uint32, uint64
|
|
31
31
|
from chia.wallet.util.compute_hints import HintedCoin
|
|
32
|
++
from chia.wallet.wallet_node import WalletNode
|
|
32
33
|
from tests.core.data_layer.util import ChiaRoot
|
|
33
34
|
|
|
34
35
|
|
|
@@@ -424,3 -424,3 +425,8 @@@ def invariant_check_mempool(mempool: Me
|
|
|
424
425
|
if val is None:
|
|
425
426
|
val = 0
|
|
426
427
|
assert mempool._total_fee == val
|
|
428
|
++
|
|
429
|
++
|
|
430
|
++
async def wallet_height_at_least(wallet_node: WalletNode, h: uint32) -> bool:
|
|
431
|
++
height = await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()
|
|
432
|
++
return height == h
|
|
@@@ -1,0 -1,0 +1,161 @@@
|
|
|
1
|
++
from __future__ import annotations
|
|
2
|
++
|
|
3
|
++
import json
|
|
4
|
++
from dataclasses import dataclass
|
|
5
|
++
from pathlib import Path
|
|
6
|
++
from typing import Any, Dict, List, Tuple
|
|
7
|
++
|
|
8
|
++
from chia_rs import Coin
|
|
9
|
++
|
|
10
|
++
from chia.consensus.constants import ConsensusConstants
|
|
11
|
++
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
12
|
++
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
13
|
++
from chia.types.condition_opcodes import ConditionOpcode
|
|
14
|
++
from chia.types.condition_with_args import ConditionWithArgs
|
|
15
|
++
from chia.types.generator_types import BlockGenerator
|
|
16
|
++
from chia.util.ints import uint32, uint64
|
|
17
|
++
from chia.wallet.cat_wallet.cat_utils import match_cat_puzzle
|
|
18
|
++
from chia.wallet.puzzles.load_clvm import load_serialized_clvm_maybe_recompile
|
|
19
|
++
from chia.wallet.uncurried_puzzle import uncurry_puzzle
|
|
20
|
++
|
|
21
|
++
DESERIALIZE_MOD = load_serialized_clvm_maybe_recompile(
|
|
22
|
++
"chialisp_deserialisation.clsp", package_or_requirement="chia.consensus.puzzles"
|
|
23
|
++
)
|
|
24
|
++
|
|
25
|
++
|
|
26
|
++
@dataclass
|
|
27
|
++
class NPC:
|
|
28
|
++
coin_name: bytes32
|
|
29
|
++
puzzle_hash: bytes32
|
|
30
|
++
conditions: List[Tuple[ConditionOpcode, List[ConditionWithArgs]]]
|
|
31
|
++
|
|
32
|
++
|
|
33
|
++
@dataclass
|
|
34
|
++
class CAT:
|
|
35
|
++
asset_id: str
|
|
36
|
++
memo: str
|
|
37
|
++
npc: NPC
|
|
38
|
++
|
|
39
|
++
def cat_to_dict(self) -> Dict[str, Any]:
|
|
40
|
++
return {"asset_id": self.asset_id, "memo": self.memo, "npc": npc_to_dict(self.npc)}
|
|
41
|
++
|
|
42
|
++
|
|
43
|
++
def condition_with_args_to_dict(condition_with_args: ConditionWithArgs) -> Dict[str, Any]:
|
|
44
|
++
return {
|
|
45
|
++
"condition_opcode": condition_with_args.opcode.name,
|
|
46
|
++
"arguments": [arg.hex() for arg in condition_with_args.vars],
|
|
47
|
++
}
|
|
48
|
++
|
|
49
|
++
|
|
50
|
++
def condition_list_to_dict(condition_list: Tuple[ConditionOpcode, List[ConditionWithArgs]]) -> List[Dict[str, Any]]:
|
|
51
|
++
assert all([condition_list[0] == cwa.opcode for cwa in condition_list[1]])
|
|
52
|
++
return [condition_with_args_to_dict(cwa) for cwa in condition_list[1]]
|
|
53
|
++
|
|
54
|
++
|
|
55
|
++
def npc_to_dict(npc: NPC) -> Dict[str, Any]:
|
|
56
|
++
return {
|
|
57
|
++
"coin_name": npc.coin_name.hex(),
|
|
58
|
++
"conditions": [{"condition_type": c[0].name, "conditions": condition_list_to_dict(c)} for c in npc.conditions],
|
|
59
|
++
"puzzle_hash": npc.puzzle_hash.hex(),
|
|
60
|
++
}
|
|
61
|
++
|
|
62
|
++
|
|
63
|
++
def run_generator(block_generator: BlockGenerator, constants: ConsensusConstants, max_cost: int) -> List[CAT]:
|
|
64
|
++
block_args = [bytes(a) for a in block_generator.generator_refs]
|
|
65
|
++
cost, block_result = block_generator.program.run_with_cost(max_cost, [DESERIALIZE_MOD, block_args])
|
|
66
|
++
|
|
67
|
++
coin_spends = block_result.first()
|
|
68
|
++
|
|
69
|
++
cat_list: List[CAT] = []
|
|
70
|
++
for spend in coin_spends.as_iter():
|
|
71
|
++
parent, puzzle, amount, solution = spend.as_iter()
|
|
72
|
++
args = match_cat_puzzle(uncurry_puzzle(puzzle))
|
|
73
|
++
|
|
74
|
++
if args is None:
|
|
75
|
++
continue
|
|
76
|
++
|
|
77
|
++
_, asset_id, _ = args
|
|
78
|
++
memo = ""
|
|
79
|
++
|
|
80
|
++
puzzle_result = puzzle.run(solution)
|
|
81
|
++
|
|
82
|
++
conds: Dict[ConditionOpcode, List[ConditionWithArgs]] = {}
|
|
83
|
++
|
|
84
|
++
for condition in puzzle_result.as_python():
|
|
85
|
++
op = ConditionOpcode(condition[0])
|
|
86
|
++
|
|
87
|
++
if op not in conds:
|
|
88
|
++
conds[op] = []
|
|
89
|
++
|
|
90
|
++
if condition[0] != ConditionOpcode.CREATE_COIN or len(condition) < 4:
|
|
91
|
++
conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
|
|
92
|
++
continue
|
|
93
|
++
|
|
94
|
++
# If only 3 elements (opcode + 2 args), there is no memo, this is ph, amount
|
|
95
|
++
if type(condition[3]) is not list:
|
|
96
|
++
# If it's not a list, it's not the correct format
|
|
97
|
++
conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
|
|
98
|
++
continue
|
|
99
|
++
|
|
100
|
++
conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]] + [condition[3][0]]))
|
|
101
|
++
|
|
102
|
++
# special retirement address
|
|
103
|
++
if condition[3][0].hex() != "0000000000000000000000000000000000000000000000000000000000000000":
|
|
104
|
++
continue
|
|
105
|
++
|
|
106
|
++
if len(condition[3]) >= 2:
|
|
107
|
++
try:
|
|
108
|
++
memo = condition[3][1].decode("utf-8", errors="strict")
|
|
109
|
++
except UnicodeError:
|
|
110
|
++
pass # ignore this error which should leave memo as empty string
|
|
111
|
++
|
|
112
|
++
# technically there could be more such create_coin ops in the list but our wallet does not
|
|
113
|
++
# so leaving it for the future
|
|
114
|
++
break
|
|
115
|
++
|
|
116
|
++
puzzle_hash = puzzle.get_tree_hash()
|
|
117
|
++
coin = Coin(bytes32(parent.as_atom()), puzzle_hash, amount.as_int())
|
|
118
|
++
cat_list.append(
|
|
119
|
++
CAT(
|
|
120
|
++
asset_id=bytes(asset_id).hex()[2:],
|
|
121
|
++
memo=memo,
|
|
122
|
++
npc=NPC(coin.name(), puzzle_hash, [(op, cond) for op, cond in conds.items()]),
|
|
123
|
++
)
|
|
124
|
++
)
|
|
125
|
++
|
|
126
|
++
return cat_list
|
|
127
|
++
|
|
128
|
++
|
|
129
|
++
def ref_list_to_args(ref_list: List[uint32], root_path: Path) -> List[SerializedProgram]:
|
|
130
|
++
args = []
|
|
131
|
++
for height in ref_list:
|
|
132
|
++
with open(root_path / f"{height}.json", "rb") as f:
|
|
133
|
++
program_str = json.load(f)["block"]["transactions_generator"]
|
|
134
|
++
args.append(SerializedProgram.fromhex(program_str))
|
|
135
|
++
return args
|
|
136
|
++
|
|
137
|
++
|
|
138
|
++
def run_generator_with_args(
|
|
139
|
++
generator_program_hex: str,
|
|
140
|
++
generator_args: List[SerializedProgram],
|
|
141
|
++
constants: ConsensusConstants,
|
|
142
|
++
cost: uint64,
|
|
143
|
++
) -> List[CAT]:
|
|
144
|
++
if not generator_program_hex:
|
|
145
|
++
return []
|
|
146
|
++
generator_program = SerializedProgram.fromhex(generator_program_hex)
|
|
147
|
++
block_generator = BlockGenerator(generator_program, generator_args, [])
|
|
148
|
++
return run_generator(block_generator, constants, min(constants.MAX_BLOCK_COST_CLVM, cost))
|
|
149
|
++
|
|
150
|
++
|
|
151
|
++
def run_json_block(full_block: Dict[str, Any], parent: Path, constants: ConsensusConstants) -> List[CAT]:
|
|
152
|
++
ref_list = full_block["block"]["transactions_generator_ref_list"]
|
|
153
|
++
tx_info: Dict[str, Any] = full_block["block"]["transactions_info"]
|
|
154
|
++
generator_program_hex: str = full_block["block"]["transactions_generator"]
|
|
155
|
++
cat_list: List[CAT] = []
|
|
156
|
++
if tx_info and generator_program_hex:
|
|
157
|
++
cost = tx_info["cost"]
|
|
158
|
++
args = ref_list_to_args(ref_list, parent)
|
|
159
|
++
cat_list = run_generator_with_args(generator_program_hex, args, constants, cost)
|
|
160
|
++
|
|
161
|
++
return cat_list
|
|
@@@ -6,7 -6,7 +6,6 @@@ from typing import Generator, Iterator
|
|
|
6
6
|
import pytest
|
|
7
7
|
from chia_rs import G1Element, G2Element
|
|
8
8
|
|
|
9
|
--
from benchmarks.utils import rand_bytes, rand_g1, rand_g2, rand_hash, rand_vdf, rand_vdf_proof, rewards
|
|
10
9
|
from chia.types.blockchain_format.foliage import Foliage, FoliageBlockData, FoliageTransactionBlock, TransactionsInfo
|
|
11
10
|
from chia.types.blockchain_format.pool_target import PoolTarget
|
|
12
11
|
from chia.types.blockchain_format.proof_of_space import ProofOfSpace
|
|
@@@ -26,6 -26,6 +25,7 @@@ from chia.types.header_block import Hea
|
|
|
26
25
|
from chia.util.full_block_utils import block_info_from_block, generator_from_block, header_block_from_block
|
|
27
26
|
from chia.util.generator_tools import get_block_header
|
|
28
27
|
from chia.util.ints import uint8, uint32, uint64, uint128
|
|
28
|
++
from tests.util.benchmarks import rand_bytes, rand_g1, rand_g2, rand_hash, rand_vdf, rand_vdf_proof, rewards
|
|
29
29
|
|
|
30
30
|
test_g2s: List[G2Element] = [rand_g2() for _ in range(10)]
|
|
31
31
|
test_g1s: List[G1Element] = [rand_g1() for _ in range(10)]
|
|
@@@ -1,14 -1,14 +1,16 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
|
--
from typing import AsyncIterator, Iterator, List
|
|
4
|
++
from typing import AsyncIterator, Iterator, List, Optional, TypeVar
|
|
5
5
|
|
|
6
|
++
import anyio
|
|
6
7
|
import pytest
|
|
7
8
|
|
|
8
9
|
from chia.util.errors import InvalidPathError
|
|
9
10
|
from chia.util.misc import (
|
|
10
11
|
SplitAsyncManager,
|
|
11
12
|
SplitManager,
|
|
13
|
++
ValuedEvent,
|
|
12
14
|
format_bytes,
|
|
13
15
|
format_minutes,
|
|
14
16
|
split_async_manager,
|
|
@@@ -16,6 -16,6 +18,9 @@@
|
|
|
16
18
|
to_batches,
|
|
17
19
|
validate_directory_writable,
|
|
18
20
|
)
|
|
21
|
++
from chia.util.timing import adjusted_timeout, backoff_times
|
|
22
|
++
|
|
23
|
++
T = TypeVar("T")
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
class TestMisc:
|
|
@@@ -306,3 -306,3 +311,103 @@@ async def test_split_async_manager_rais
|
|
|
306
311
|
|
|
307
312
|
with pytest.raises(Exception, match="^not yet entered$"):
|
|
308
313
|
await split.exit()
|
|
314
|
++
|
|
315
|
++
|
|
316
|
++
async def wait_for_valued_event_waiters(
|
|
317
|
++
event: ValuedEvent[T],
|
|
318
|
++
count: int,
|
|
319
|
++
timeout: float = 10,
|
|
320
|
++
) -> None:
|
|
321
|
++
with anyio.fail_after(delay=adjusted_timeout(timeout)):
|
|
322
|
++
for delay in backoff_times():
|
|
323
|
++
# ignoring the type since i'm hacking into the private attribute
|
|
324
|
++
# hopefully this is ok for testing and if it becomes invalid we
|
|
325
|
++
# will end up with an exception and can adjust then
|
|
326
|
++
if len(event._event._waiters) >= count: # type: ignore[attr-defined]
|
|
327
|
++
return
|
|
328
|
++
await anyio.sleep(delay)
|
|
329
|
++
|
|
330
|
++
|
|
331
|
++
@pytest.mark.anyio
|
|
332
|
++
async def test_valued_event_wait_already_set() -> None:
|
|
333
|
++
valued_event = ValuedEvent[int]()
|
|
334
|
++
value = 37
|
|
335
|
++
valued_event.set(value)
|
|
336
|
++
|
|
337
|
++
with anyio.fail_after(adjusted_timeout(10)):
|
|
338
|
++
result = await valued_event.wait()
|
|
339
|
++
|
|
340
|
++
assert result == value
|
|
341
|
++
|
|
342
|
++
|
|
343
|
++
@pytest.mark.anyio
|
|
344
|
++
async def test_valued_event_wait_not_yet_set() -> None:
|
|
345
|
++
valued_event = ValuedEvent[int]()
|
|
346
|
++
value = 37
|
|
347
|
++
result: Optional[int] = None
|
|
348
|
++
|
|
349
|
++
async def wait(valued_event: ValuedEvent[int]) -> None:
|
|
350
|
++
nonlocal result
|
|
351
|
++
result = await valued_event.wait()
|
|
352
|
++
|
|
353
|
++
with anyio.fail_after(adjusted_timeout(10)):
|
|
354
|
++
async with anyio.create_task_group() as task_group:
|
|
355
|
++
task_group.start_soon(wait, valued_event)
|
|
356
|
++
await wait_for_valued_event_waiters(event=valued_event, count=1)
|
|
357
|
++
valued_event.set(value)
|
|
358
|
++
|
|
359
|
++
assert result == value
|
|
360
|
++
|
|
361
|
++
|
|
362
|
++
@pytest.mark.anyio
|
|
363
|
++
async def test_valued_event_wait_blocks_when_not_set() -> None:
|
|
364
|
++
valued_event = ValuedEvent[int]()
|
|
365
|
++
with pytest.raises(TimeoutError):
|
|
366
|
++
# if we could just process until there are no pending events, that would be great
|
|
367
|
++
with anyio.fail_after(adjusted_timeout(1)):
|
|
368
|
++
await valued_event.wait()
|
|
369
|
++
|
|
370
|
++
|
|
371
|
++
@pytest.mark.anyio
|
|
372
|
++
async def test_valued_event_multiple_waits_all_get_values() -> None:
|
|
373
|
++
results: List[int] = []
|
|
374
|
++
valued_event = ValuedEvent[int]()
|
|
375
|
++
value = 37
|
|
376
|
++
task_count = 10
|
|
377
|
++
|
|
378
|
++
async def wait_and_append() -> None:
|
|
379
|
++
results.append(await valued_event.wait())
|
|
380
|
++
|
|
381
|
++
async with anyio.create_task_group() as task_group:
|
|
382
|
++
for i in range(task_count):
|
|
383
|
++
task_group.start_soon(wait_and_append, name=f"wait_and_append_{i}")
|
|
384
|
++
|
|
385
|
++
await wait_for_valued_event_waiters(event=valued_event, count=task_count)
|
|
386
|
++
valued_event.set(value)
|
|
387
|
++
|
|
388
|
++
assert results == [value] * task_count
|
|
389
|
++
|
|
390
|
++
|
|
391
|
++
@pytest.mark.anyio
|
|
392
|
++
async def test_valued_event_set_again_raises_and_does_not_change_value() -> None:
|
|
393
|
++
valued_event = ValuedEvent[int]()
|
|
394
|
++
value = 37
|
|
395
|
++
valued_event.set(value)
|
|
396
|
++
|
|
397
|
++
with pytest.raises(Exception, match="^Value already set$"):
|
|
398
|
++
valued_event.set(value + 1)
|
|
399
|
++
|
|
400
|
++
with anyio.fail_after(adjusted_timeout(10)):
|
|
401
|
++
result = await valued_event.wait()
|
|
402
|
++
|
|
403
|
++
assert result == value
|
|
404
|
++
|
|
405
|
++
|
|
406
|
++
@pytest.mark.anyio
|
|
407
|
++
async def test_valued_event_wait_raises_if_not_set() -> None:
|
|
408
|
++
valued_event = ValuedEvent[int]()
|
|
409
|
++
valued_event._event.set()
|
|
410
|
++
|
|
411
|
++
with pytest.raises(Exception, match="^Value not set despite event being set$"):
|
|
412
|
++
with anyio.fail_after(adjusted_timeout(10)):
|
|
413
|
++
await valued_event.wait()
|
|
@@@ -3,7 -3,7 +3,7 @@@ from __future__ import annotation
|
|
|
3
3
|
|
|
4
4
|
import ast
|
|
5
5
|
import inspect
|
|
6
|
--
from typing import Any, Set, cast
|
|
6
|
++
from typing import Any, Dict, Set, cast
|
|
7
7
|
|
|
8
8
|
from chia.protocols import (
|
|
9
9
|
farmer_protocol,
|
|
@@@ -11,6 -11,6 +11,7 @@@
|
|
|
11
11
|
harvester_protocol,
|
|
12
12
|
introducer_protocol,
|
|
13
13
|
pool_protocol,
|
|
14
|
++
protocol_message_types,
|
|
14
15
|
shared_protocol,
|
|
15
16
|
timelord_protocol,
|
|
16
17
|
wallet_protocol,
|
|
@@@ -47,6 -47,6 +48,30 @@@ def test_missing_messages_state_machine
|
|
|
47
48
|
), "A message was added to the protocol state machine. Make sure to update the protocol message regression test to include the new message"
|
|
48
49
|
|
|
49
50
|
|
|
51
|
++
def test_message_ids() -> None:
|
|
52
|
++
parsed = ast.parse(inspect.getsource(protocol_message_types))
|
|
53
|
++
message_ids: Dict[int, str] = {}
|
|
54
|
++
for line in parsed.body:
|
|
55
|
++
if not isinstance(line, ast.ClassDef) or line.name != "ProtocolMessageTypes":
|
|
56
|
++
continue
|
|
57
|
++
for entry in line.body:
|
|
58
|
++
if not isinstance(entry, ast.Assign): # pragma: no cover
|
|
59
|
++
continue
|
|
60
|
++
assert isinstance(entry.value, ast.Constant)
|
|
61
|
++
assert isinstance(entry.targets[0], ast.Name)
|
|
62
|
++
message_id = entry.value.value
|
|
63
|
++
message_name = entry.targets[0].id
|
|
64
|
++
if message_id in message_ids: # pragma: no cover
|
|
65
|
++
raise AssertionError(
|
|
66
|
++
f'protocol message ID clash between "{message_name}" and "{message_ids[message_id]}". Value {message_id}'
|
|
67
|
++
)
|
|
68
|
++
message_ids[message_id] = message_name
|
|
69
|
++
if message_id < 0 or message_id > 255: # pragma: no cover
|
|
70
|
++
raise AssertionError(f'message ID must fit in a uint8. "{message_name}" has value {message_id}')
|
|
71
|
++
break
|
|
72
|
++
assert len(message_ids) > 0
|
|
73
|
++
|
|
74
|
++
|
|
50
75
|
def test_missing_messages() -> None:
|
|
51
76
|
wallet_msgs = {
|
|
52
77
|
"CoinState",
|
|
@@@ -1,0 -1,0 +1,116 @@@
|
|
|
1
|
++
from __future__ import annotations
|
|
2
|
++
|
|
3
|
++
import copy
|
|
4
|
++
from dataclasses import dataclass
|
|
5
|
++
from typing import List, Optional, Union
|
|
6
|
++
|
|
7
|
++
import pytest
|
|
8
|
++
|
|
9
|
++
from chia.util.recursive_replace import recursive_replace
|
|
10
|
++
|
|
11
|
++
|
|
12
|
++
class TestC:
|
|
13
|
++
a: int
|
|
14
|
++
b: str
|
|
15
|
++
|
|
16
|
++
def __init__(self, a: int, b: str):
|
|
17
|
++
self.a = a
|
|
18
|
++
self.b = b
|
|
19
|
++
|
|
20
|
++
# WARNING: this is just a simple stand in for rust classes and is not a good
|
|
21
|
++
# reference for how such a method should be implemented in python
|
|
22
|
++
def replace(self, **kwargs: Union[int, str, Optional[TestA]]) -> TestC:
|
|
23
|
++
ret = TestC(copy.deepcopy(self.a), copy.deepcopy(self.b))
|
|
24
|
++
for key, value in kwargs.items():
|
|
25
|
++
if key == "a":
|
|
26
|
++
ret.a = value # type: ignore[assignment]
|
|
27
|
++
elif key == "b": # pragma: no cover
|
|
28
|
++
ret.b = value # type: ignore[assignment]
|
|
29
|
++
else: # pragma: no cover
|
|
30
|
++
raise TypeError(f"unknown field {key}")
|
|
31
|
++
return ret
|
|
32
|
++
|
|
33
|
++
|
|
34
|
++
@dataclass
|
|
35
|
++
class TestA:
|
|
36
|
++
a: int
|
|
37
|
++
b: str
|
|
38
|
++
c: List[int]
|
|
39
|
++
d: Optional[TestC]
|
|
40
|
++
|
|
41
|
++
|
|
42
|
++
class TestB:
|
|
43
|
++
a: int
|
|
44
|
++
b: str
|
|
45
|
++
c: Optional[TestA]
|
|
46
|
++
|
|
47
|
++
def __init__(self, a: int, b: str, c: Optional[TestA]):
|
|
48
|
++
self.a = a
|
|
49
|
++
self.b = b
|
|
50
|
++
self.c = c
|
|
51
|
++
|
|
52
|
++
# WARNING: this is just a simple stand in for rust classes and is not a good
|
|
53
|
++
# reference for how such a method should be implemented in python
|
|
54
|
++
def replace(self, **kwargs: Union[int, str, Optional[TestA]]) -> TestB:
|
|
55
|
++
ret = TestB(copy.deepcopy(self.a), copy.deepcopy(self.b), copy.deepcopy(self.c))
|
|
56
|
++
for key, value in kwargs.items():
|
|
57
|
++
if key == "a": # pragma: no cover
|
|
58
|
++
ret.a = value # type: ignore[assignment]
|
|
59
|
++
elif key == "b":
|
|
60
|
++
ret.b = value # type: ignore[assignment]
|
|
61
|
++
elif key == "c":
|
|
62
|
++
ret.c = value # type: ignore[assignment]
|
|
63
|
++
else:
|
|
64
|
++
raise TypeError(f"unknown field {key}")
|
|
65
|
++
return ret
|
|
66
|
++
|
|
67
|
++
def __eq__(self, other: object) -> bool:
|
|
68
|
++
if isinstance(other, TestB):
|
|
69
|
++
return self.a == other.a and self.b == other.b and self.c == self.c
|
|
70
|
++
else:
|
|
71
|
++
return False # pragma: no cover
|
|
72
|
++
|
|
73
|
++
|
|
74
|
++
def test_recursive_replace_dataclass() -> None:
|
|
75
|
++
a = TestA(42, "foobar", [1337, 42], None)
|
|
76
|
++
a2 = recursive_replace(a, "b", "barfoo")
|
|
77
|
++
|
|
78
|
++
assert a.a == a2.a
|
|
79
|
++
assert a.b == "foobar"
|
|
80
|
++
assert a2.b == "barfoo"
|
|
81
|
++
assert a.c == a2.c
|
|
82
|
++
|
|
83
|
++
|
|
84
|
++
def test_recursive_replace_other() -> None:
|
|
85
|
++
b = TestB(42, "foobar", None)
|
|
86
|
++
b2 = recursive_replace(b, "b", "barfoo")
|
|
87
|
++
|
|
88
|
++
assert b.a == b2.a
|
|
89
|
++
assert b.b == "foobar"
|
|
90
|
++
assert b2.b == "barfoo"
|
|
91
|
++
assert b.c == b2.c
|
|
92
|
++
|
|
93
|
++
|
|
94
|
++
def test_recursive_replace() -> None:
|
|
95
|
++
b1 = TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], None))
|
|
96
|
++
b2 = recursive_replace(b1, "c.a", 110)
|
|
97
|
++
|
|
98
|
++
assert b1 == TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], None))
|
|
99
|
++
assert b2 == TestB(42, "foobar", TestA(110, "barfoo", [1, 2, 3], None))
|
|
100
|
++
|
|
101
|
++
|
|
102
|
++
def test_recursive_replace2() -> None:
|
|
103
|
++
b1 = TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], TestC(123, "345")))
|
|
104
|
++
b2 = recursive_replace(b1, "c.d.a", 110)
|
|
105
|
++
|
|
106
|
++
assert b1 == TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], TestC(123, "345")))
|
|
107
|
++
assert b2 == TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], TestC(110, "345")))
|
|
108
|
++
|
|
109
|
++
|
|
110
|
++
def test_recursive_replace_unknown() -> None:
|
|
111
|
++
b = TestB(42, "foobar", TestA(1337, "barfoo", [1, 2, 3], None))
|
|
112
|
++
with pytest.raises(TypeError):
|
|
113
|
++
recursive_replace(b, "c.foobar", 110)
|
|
114
|
++
|
|
115
|
++
with pytest.raises(TypeError):
|
|
116
|
++
recursive_replace(b, "foobar", 110)
|
|
@@@ -3,7 -3,7 +3,6 @@@ from __future__ import annotation
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import tempfile
|
|
5
5
|
from pathlib import Path
|
|
6
|
--
from typing import List
|
|
7
6
|
|
|
8
7
|
import pytest
|
|
9
8
|
|
|
@@@ -11,7 -11,7 +10,6 @@@ from chia.consensus.block_rewards impor
|
|
|
11
10
|
from chia.protocols.wallet_protocol import CoinState
|
|
12
11
|
from chia.rpc.wallet_rpc_api import WalletRpcApi
|
|
13
12
|
from chia.rpc.wallet_rpc_client import WalletRpcClient
|
|
14
|
--
from chia.simulator.full_node_simulator import FullNodeSimulator
|
|
15
13
|
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol
|
|
16
14
|
from chia.types.blockchain_format.coin import Coin, coin_as_list
|
|
17
15
|
from chia.types.blockchain_format.program import Program
|
|
@@@ -29,14 -29,14 +27,14 @@@ from chia.wallet.derivation_record impo
|
|
|
29
27
|
from chia.wallet.derive_keys import _derive_path_unhardened, master_sk_to_wallet_sk_unhardened_intermediate
|
|
30
28
|
from chia.wallet.lineage_proof import LineageProof
|
|
31
29
|
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_hash_for_pk
|
|
32
|
--
from chia.wallet.transaction_record import TransactionRecord
|
|
33
30
|
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
|
|
34
31
|
from chia.wallet.util.wallet_types import WalletType
|
|
35
32
|
from chia.wallet.wallet_info import WalletInfo
|
|
36
33
|
from chia.wallet.wallet_interested_store import WalletInterestedStore
|
|
37
34
|
from chia.wallet.wallet_node import WalletNode
|
|
35
|
++
from chia.wallet.wallet_state_manager import WalletStateManager
|
|
38
36
|
from tests.conftest import ConsensusMode
|
|
39
|
--
from tests.util.setup_nodes import SimulatorsAndWalletsServices
|
|
37
|
++
from tests.util.setup_nodes import OldSimulatorsAndWallets, SimulatorsAndWalletsServices
|
|
40
38
|
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
|
|
41
39
|
|
|
42
40
|
|
|
@@@ -44,1016 -44,1016 +42,955 @@@ def check_wallets(node: WalletNode) ->
|
|
|
44
42
|
return len(node.wallet_state_manager.wallets.keys())
|
|
45
43
|
|
|
46
44
|
|
|
47
|
--
|
|
48
|
--
|
|
49
|
--
|
|
50
|
--
|
|
51
|
--
|
|
52
|
--
|
|
53
|
--
|
|
54
|
--
|
|
55
|
--
|
|
56
|
--
|
|
57
|
--
|
|
58
|
--
|
|
59
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
60
|
--
|
|
61
|
--
ph = await wallet.get_new_puzzlehash()
|
|
62
|
--
if trusted:
|
|
63
|
--
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
64
|
--
else:
|
|
65
|
--
wallet_node.config["trusted_peers"] = {}
|
|
66
|
--
|
|
67
|
--
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
68
|
--
for i in range(0, num_blocks):
|
|
69
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
70
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
|
|
71
|
--
|
|
72
|
--
funds = sum(
|
|
73
|
--
[
|
|
74
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
75
|
--
for i in range(1, num_blocks + 1)
|
|
76
|
--
]
|
|
77
|
--
)
|
|
78
|
--
|
|
79
|
--
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
80
|
--
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
81
|
--
|
|
82
|
--
async with wallet_node.wallet_state_manager.lock:
|
|
83
|
--
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
84
|
--
wallet_node.wallet_state_manager,
|
|
85
|
--
wallet,
|
|
86
|
--
{"identifier": "genesis_by_id"},
|
|
87
|
--
uint64(100),
|
|
88
|
--
DEFAULT_TX_CONFIG,
|
|
89
|
--
fee=uint64(10),
|
|
90
|
--
)
|
|
91
|
--
# The next 2 lines are basically a noop, it just adds test coverage
|
|
92
|
--
cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, cat_wallet.wallet_info)
|
|
93
|
--
await wallet_node.wallet_state_manager.add_new_wallet(cat_wallet)
|
|
94
|
--
|
|
95
|
--
tx_queue: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
96
|
--
tx_record = tx_queue[0]
|
|
97
|
--
await full_node_api.process_transaction_records(records=[tx_record])
|
|
98
|
--
|
|
99
|
--
await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
|
|
100
|
--
await time_out_assert(20, cat_wallet.get_spendable_balance, 100)
|
|
101
|
--
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
|
|
102
|
--
await time_out_assert(20, wallet.get_confirmed_balance, funds - 110)
|
|
103
|
--
await time_out_assert(20, wallet.get_spendable_balance, funds - 110)
|
|
104
|
--
await time_out_assert(20, wallet.get_unconfirmed_balance, funds - 110)
|
|
105
|
--
|
|
106
|
--
# Test migration
|
|
107
|
--
all_lineage = await cat_wallet.lineage_store.get_all_lineage_proofs()
|
|
108
|
--
current_info = cat_wallet.wallet_info
|
|
109
|
--
data_str = bytes(
|
|
110
|
--
LegacyCATInfo(
|
|
111
|
--
cat_wallet.cat_info.limitations_program_hash, cat_wallet.cat_info.my_tail, list(all_lineage.items())
|
|
112
|
--
)
|
|
113
|
--
).hex()
|
|
114
|
--
wallet_info = WalletInfo(current_info.id, current_info.name, current_info.type, data_str)
|
|
115
|
--
new_cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, wallet_info)
|
|
116
|
--
assert new_cat_wallet.cat_info.limitations_program_hash == cat_wallet.cat_info.limitations_program_hash
|
|
117
|
--
assert new_cat_wallet.cat_info.my_tail == cat_wallet.cat_info.my_tail
|
|
118
|
--
assert await cat_wallet.lineage_store.get_all_lineage_proofs() == all_lineage
|
|
119
|
--
|
|
120
|
--
height = full_node_api.full_node.blockchain.get_peak_height()
|
|
121
|
--
await full_node_api.reorg_from_index_to_new_index(
|
|
122
|
--
ReorgProtocol(height - num_blocks - 1, height + 1, 32 * b"1", None)
|
|
123
|
--
)
|
|
124
|
--
await time_out_assert(20, cat_wallet.get_confirmed_balance, 0)
|
|
125
|
--
|
|
126
|
--
@pytest.mark.anyio
|
|
127
|
--
async def test_cat_creation_unique_lineage_store(self, self_hostname, two_wallet_nodes):
|
|
128
|
--
num_blocks = 3
|
|
129
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
130
|
--
full_node_api = full_nodes[0]
|
|
131
|
--
full_node_server = full_node_api.server
|
|
132
|
--
wallet_node, wallet_server = wallets[0]
|
|
133
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
134
|
--
ph = await wallet.get_new_puzzlehash()
|
|
45
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
46
|
++
@pytest.mark.anyio
|
|
47
|
++
async def test_cat_creation(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
48
|
++
num_blocks = 3
|
|
49
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
50
|
++
full_node_api = full_nodes[0]
|
|
51
|
++
full_node_server = full_node_api.server
|
|
52
|
++
wallet_node, server_2 = wallets[0]
|
|
53
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
54
|
++
|
|
55
|
++
ph = await wallet.get_new_puzzlehash()
|
|
56
|
++
if trusted:
|
|
135
57
|
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
58
|
++
else:
|
|
59
|
++
wallet_node.config["trusted_peers"] = {}
|
|
136
60
|
|
|
137
|
--
|
|
138
|
--
|
|
139
|
--
|
|
140
|
--
|
|
61
|
++
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
62
|
++
for _ in range(num_blocks):
|
|
63
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
64
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
141
65
|
|
|
142
|
--
|
|
143
|
--
|
|
144
|
--
|
|
145
|
--
|
|
146
|
--
|
|
147
|
--
|
|
66
|
++
funds = sum(
|
|
67
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
68
|
++
)
|
|
69
|
++
|
|
70
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
71
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
148
72
|
|
|
149
|
--
|
|
150
|
--
await
|
|
151
|
--
|
|
152
|
--
|
|
153
|
--
|
|
154
|
--
|
|
155
|
--
|
|
156
|
--
|
|
157
|
--
|
|
158
|
--
|
|
159
|
--
|
|
160
|
--
|
|
161
|
--
|
|
162
|
--
|
|
163
|
--
|
|
164
|
--
|
|
165
|
--
|
|
166
|
--
|
|
167
|
--
|
|
168
|
--
|
|
169
|
--
|
|
170
|
--
|
|
171
|
--
|
|
172
|
--
|
|
173
|
--
|
|
174
|
--
|
|
175
|
--
|
|
176
|
--
|
|
73
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
74
|
++
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
75
|
++
wallet_node.wallet_state_manager,
|
|
76
|
++
wallet,
|
|
77
|
++
{"identifier": "genesis_by_id"},
|
|
78
|
++
uint64(100),
|
|
79
|
++
DEFAULT_TX_CONFIG,
|
|
80
|
++
fee=uint64(10),
|
|
81
|
++
)
|
|
82
|
++
# The next 2 lines are basically a noop, it just adds test coverage
|
|
83
|
++
cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, cat_wallet.wallet_info)
|
|
84
|
++
await wallet_node.wallet_state_manager.add_new_wallet(cat_wallet)
|
|
85
|
++
|
|
86
|
++
tx_queue = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
87
|
++
tx_record = tx_queue[0]
|
|
88
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
89
|
++
|
|
90
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
|
|
91
|
++
await time_out_assert(20, cat_wallet.get_spendable_balance, 100)
|
|
92
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
|
|
93
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds - 110)
|
|
94
|
++
await time_out_assert(20, wallet.get_spendable_balance, funds - 110)
|
|
95
|
++
await time_out_assert(20, wallet.get_unconfirmed_balance, funds - 110)
|
|
96
|
++
|
|
97
|
++
# Test migration
|
|
98
|
++
all_lineage = await cat_wallet.lineage_store.get_all_lineage_proofs()
|
|
99
|
++
current_info = cat_wallet.wallet_info
|
|
100
|
++
data_str = bytes(
|
|
101
|
++
LegacyCATInfo(
|
|
102
|
++
cat_wallet.cat_info.limitations_program_hash, cat_wallet.cat_info.my_tail, list(all_lineage.items())
|
|
103
|
++
)
|
|
104
|
++
).hex()
|
|
105
|
++
wallet_info = WalletInfo(current_info.id, current_info.name, current_info.type, data_str)
|
|
106
|
++
new_cat_wallet = await CATWallet.create(wallet_node.wallet_state_manager, wallet, wallet_info)
|
|
107
|
++
assert new_cat_wallet.cat_info.limitations_program_hash == cat_wallet.cat_info.limitations_program_hash
|
|
108
|
++
assert new_cat_wallet.cat_info.my_tail == cat_wallet.cat_info.my_tail
|
|
109
|
++
assert await cat_wallet.lineage_store.get_all_lineage_proofs() == all_lineage
|
|
110
|
++
|
|
111
|
++
height = full_node_api.full_node.blockchain.get_peak_height()
|
|
112
|
++
assert height is not None
|
|
113
|
++
await full_node_api.reorg_from_index_to_new_index(
|
|
114
|
++
ReorgProtocol(uint32(height - num_blocks - 1), uint32(height + 1), bytes32(32 * b"1"), None)
|
|
177
115
|
)
|
|
178
|
--
|
|
179
|
--
async def test_cat_spend(self, self_hostname, two_wallet_nodes, trusted):
|
|
180
|
--
num_blocks = 3
|
|
181
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
182
|
--
full_node_api = full_nodes[0]
|
|
183
|
--
full_node_server = full_node_api.server
|
|
184
|
--
wallet_node, server_2 = wallets[0]
|
|
185
|
--
wallet_node_2, server_3 = wallets[1]
|
|
186
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
187
|
--
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
188
|
--
api_0 = WalletRpcApi(wallet_node)
|
|
189
|
--
api_1 = WalletRpcApi(wallet_node_2)
|
|
190
|
--
ph = await wallet.get_new_puzzlehash()
|
|
191
|
--
if trusted:
|
|
192
|
--
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
193
|
--
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
194
|
--
else:
|
|
195
|
--
wallet_node.config["trusted_peers"] = {}
|
|
196
|
--
wallet_node_2.config["trusted_peers"] = {}
|
|
197
|
--
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
198
|
--
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
199
|
--
|
|
200
|
--
for i in range(0, num_blocks):
|
|
201
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
202
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
|
|
203
|
--
|
|
204
|
--
funds = sum(
|
|
205
|
--
[
|
|
206
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
207
|
--
for i in range(1, num_blocks + 1)
|
|
208
|
--
]
|
|
209
|
--
)
|
|
116
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 0)
|
|
210
117
|
|
|
211
|
--
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
212
118
|
|
|
213
|
--
|
|
214
|
--
|
|
215
|
--
|
|
216
|
--
|
|
217
|
--
|
|
218
|
--
|
|
219
|
--
|
|
220
|
--
|
|
221
|
--
|
|
222
|
--
|
|
223
|
--
|
|
119
|
++
@pytest.mark.anyio
|
|
120
|
++
async def test_cat_creation_unique_lineage_store(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets) -> None:
|
|
121
|
++
num_blocks = 3
|
|
122
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
123
|
++
full_node_api = full_nodes[0]
|
|
124
|
++
full_node_server = full_node_api.server
|
|
125
|
++
wallet_node, wallet_server = wallets[0]
|
|
126
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
127
|
++
ph = await wallet.get_new_puzzlehash()
|
|
128
|
++
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
129
|
++
|
|
130
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
131
|
++
for _ in range(num_blocks):
|
|
132
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
133
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
224
134
|
|
|
225
|
--
|
|
226
|
--
|
|
135
|
++
funds = sum(
|
|
136
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
137
|
++
)
|
|
227
138
|
|
|
228
|
--
|
|
229
|
--
|
|
139
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
140
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
230
141
|
|
|
231
|
--
|
|
232
|
--
|
|
142
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
143
|
++
cat_wallet_1, _ = await CATWallet.create_new_cat_wallet(
|
|
144
|
++
wallet_node.wallet_state_manager,
|
|
145
|
++
wallet,
|
|
146
|
++
{"identifier": "genesis_by_id"},
|
|
147
|
++
uint64(100),
|
|
148
|
++
DEFAULT_TX_CONFIG,
|
|
149
|
++
)
|
|
150
|
++
cat_wallet_2, _ = await CATWallet.create_new_cat_wallet(
|
|
151
|
++
wallet_node.wallet_state_manager,
|
|
152
|
++
wallet,
|
|
153
|
++
{"identifier": "genesis_by_id"},
|
|
154
|
++
uint64(200),
|
|
155
|
++
DEFAULT_TX_CONFIG,
|
|
233
156
|
)
|
|
234
157
|
|
|
235
|
--
|
|
158
|
++
proofs_1 = await cat_wallet_1.lineage_store.get_all_lineage_proofs()
|
|
159
|
++
proofs_2 = await cat_wallet_2.lineage_store.get_all_lineage_proofs()
|
|
160
|
++
assert len(proofs_1) == len(proofs_2)
|
|
161
|
++
assert proofs_1 != proofs_2
|
|
162
|
++
assert cat_wallet_1.lineage_store.table_name != cat_wallet_2.lineage_store.table_name
|
|
236
163
|
|
|
237
|
--
cat_2_hash = await cat_wallet_2.get_new_inner_hash()
|
|
238
|
--
tx_records = await cat_wallet.generate_signed_transaction(
|
|
239
|
--
[uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, fee=uint64(1)
|
|
240
|
--
)
|
|
241
|
--
tx_id = None
|
|
242
|
--
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
243
|
--
for tx_record in tx_records:
|
|
244
|
--
if tx_record.wallet_id is cat_wallet.id():
|
|
245
|
--
tx_id = tx_record.name.hex()
|
|
246
|
--
assert tx_record.to_puzzle_hash == cat_2_hash
|
|
247
|
--
|
|
248
|
--
await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
|
|
249
|
--
|
|
250
|
--
await time_out_assert(20, cat_wallet.get_pending_change_balance, 40)
|
|
251
|
--
memos = await api_0.get_transaction_memo(dict(transaction_id=tx_id))
|
|
252
|
--
assert len(memos[tx_id]) == 2 # One for tx, one for change
|
|
253
|
--
assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
|
|
254
|
--
|
|
255
|
--
for i in range(1, num_blocks):
|
|
256
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
|
|
257
|
--
|
|
258
|
--
await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
|
|
259
|
--
|
|
260
|
--
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
261
|
--
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
|
|
262
|
--
|
|
263
|
--
await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 60)
|
|
264
|
--
await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
265
|
--
coins = await cat_wallet_2.select_coins(uint64(60), DEFAULT_COIN_SELECTION_CONFIG)
|
|
266
|
--
assert len(coins) == 1
|
|
267
|
--
coin = coins.pop()
|
|
268
|
--
tx_id = coin.name().hex()
|
|
269
|
--
memos = await api_1.get_transaction_memo(dict(transaction_id=tx_id))
|
|
270
|
--
assert len(memos[tx_id]) == 2
|
|
271
|
--
assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
|
|
272
|
--
cat_hash = await cat_wallet.get_new_inner_hash()
|
|
273
|
--
tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
|
|
274
|
--
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
275
|
--
|
|
276
|
--
await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
|
|
277
164
|
|
|
165
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
166
|
++
@pytest.mark.anyio
|
|
167
|
++
async def test_cat_spend(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
168
|
++
num_blocks = 3
|
|
169
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
170
|
++
full_node_api = full_nodes[0]
|
|
171
|
++
full_node_server = full_node_api.server
|
|
172
|
++
wallet_node, server_2 = wallets[0]
|
|
173
|
++
wallet_node_2, server_3 = wallets[1]
|
|
174
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
175
|
++
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
176
|
++
api_0 = WalletRpcApi(wallet_node)
|
|
177
|
++
api_1 = WalletRpcApi(wallet_node_2)
|
|
178
|
++
ph = await wallet.get_new_puzzlehash()
|
|
179
|
++
if trusted:
|
|
180
|
++
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
181
|
++
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
182
|
++
else:
|
|
183
|
++
wallet_node.config["trusted_peers"] = {}
|
|
184
|
++
wallet_node_2.config["trusted_peers"] = {}
|
|
185
|
++
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
186
|
++
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
187
|
++
|
|
188
|
++
for _ in range(num_blocks):
|
|
278
189
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
190
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
279
191
|
|
|
280
|
--
|
|
281
|
--
|
|
192
|
++
funds = sum(
|
|
193
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
194
|
++
)
|
|
282
195
|
|
|
283
|
--
|
|
284
|
--
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(height - 1, height + 1, 32 * b"1", None))
|
|
285
|
--
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
196
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
286
197
|
|
|
287
|
--
|
|
288
|
--
|
|
289
|
--
|
|
290
|
--
|
|
291
|
--
|
|
292
|
--
|
|
293
|
--
|
|
294
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
295
|
--
full_node_api = full_nodes[0]
|
|
296
|
--
full_node_server = full_node_api.server
|
|
297
|
--
wallet_node, server_2 = wallets[0]
|
|
298
|
--
wallet_node_2, server_3 = wallets[1]
|
|
299
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
300
|
--
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
301
|
--
|
|
302
|
--
ph = await wallet.get_new_puzzlehash()
|
|
303
|
--
if trusted:
|
|
304
|
--
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
305
|
--
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
306
|
--
else:
|
|
307
|
--
wallet_node.config["trusted_peers"] = {}
|
|
308
|
--
wallet_node_2.config["trusted_peers"] = {}
|
|
309
|
--
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
310
|
--
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
311
|
--
|
|
312
|
--
for i in range(0, num_blocks):
|
|
313
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
314
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
|
|
315
|
--
|
|
316
|
--
funds = sum(
|
|
317
|
--
[
|
|
318
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
319
|
--
for i in range(1, num_blocks + 1)
|
|
320
|
--
]
|
|
198
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
199
|
++
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
200
|
++
wallet_node.wallet_state_manager,
|
|
201
|
++
wallet,
|
|
202
|
++
{"identifier": "genesis_by_id"},
|
|
203
|
++
uint64(100),
|
|
204
|
++
DEFAULT_TX_CONFIG,
|
|
321
205
|
)
|
|
206
|
++
tx_queue = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
207
|
++
tx_record = tx_queue[0]
|
|
208
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
322
209
|
|
|
323
|
--
|
|
210
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
|
|
211
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
|
|
324
212
|
|
|
325
|
--
|
|
326
|
--
|
|
327
|
--
wallet_node.wallet_state_manager,
|
|
328
|
--
wallet,
|
|
329
|
--
{"identifier": "genesis_by_id"},
|
|
330
|
--
uint64(100),
|
|
331
|
--
DEFAULT_TX_CONFIG,
|
|
332
|
--
)
|
|
333
|
--
tx_queue: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
334
|
--
tx_record = tx_queue[0]
|
|
335
|
--
await full_node_api.process_transaction_records(records=[tx_record])
|
|
213
|
++
assert cat_wallet.cat_info.limitations_program_hash is not None
|
|
214
|
++
asset_id = cat_wallet.get_asset_id()
|
|
336
215
|
|
|
337
|
--
|
|
338
|
--
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
|
|
216
|
++
cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet2, asset_id)
|
|
339
217
|
|
|
340
|
--
|
|
341
|
--
asset_id = cat_wallet.get_asset_id()
|
|
218
|
++
assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
|
|
342
219
|
|
|
343
|
--
|
|
344
|
--
|
|
345
|
--
)
|
|
220
|
++
cat_2_hash = await cat_wallet_2.get_new_inner_hash()
|
|
221
|
++
tx_records = await cat_wallet.generate_signed_transaction(
|
|
222
|
++
[uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, fee=uint64(1)
|
|
223
|
++
)
|
|
224
|
++
tx_id = None
|
|
225
|
++
tx_records = await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
226
|
++
for tx_record in tx_records:
|
|
227
|
++
if tx_record.wallet_id is cat_wallet.id():
|
|
228
|
++
tx_id = tx_record.name.hex()
|
|
229
|
++
assert tx_record.to_puzzle_hash == cat_2_hash
|
|
230
|
++
|
|
231
|
++
await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
|
|
232
|
++
|
|
233
|
++
await time_out_assert(20, cat_wallet.get_pending_change_balance, 40)
|
|
234
|
++
assert tx_id is not None
|
|
235
|
++
memos = await api_0.get_transaction_memo({"transaction_id": tx_id})
|
|
236
|
++
assert len(memos[tx_id]) == 2 # One for tx, one for change
|
|
237
|
++
assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
|
|
238
|
++
|
|
239
|
++
for _ in range(1, num_blocks):
|
|
240
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"\0")))
|
|
241
|
++
|
|
242
|
++
await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
|
|
243
|
++
|
|
244
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
245
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
|
|
246
|
++
|
|
247
|
++
await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 60)
|
|
248
|
++
await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
249
|
++
coins = await cat_wallet_2.select_coins(uint64(60), DEFAULT_COIN_SELECTION_CONFIG)
|
|
250
|
++
assert len(coins) == 1
|
|
251
|
++
coin = coins.pop()
|
|
252
|
++
tx_id = coin.name().hex()
|
|
253
|
++
memos = await api_1.get_transaction_memo(dict(transaction_id=tx_id))
|
|
254
|
++
assert len(memos[tx_id]) == 2
|
|
255
|
++
assert list(memos[tx_id].values())[0][0] == cat_2_hash.hex()
|
|
256
|
++
cat_hash = await cat_wallet.get_new_inner_hash()
|
|
257
|
++
tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
|
|
258
|
++
tx_records = await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
259
|
++
|
|
260
|
++
await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
|
|
261
|
++
|
|
262
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
263
|
++
|
|
264
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 55)
|
|
265
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 55)
|
|
266
|
++
|
|
267
|
++
height = full_node_api.full_node.blockchain.get_peak_height()
|
|
268
|
++
assert height is not None
|
|
269
|
++
await full_node_api.reorg_from_index_to_new_index(
|
|
270
|
++
ReorgProtocol(uint32(height - 1), uint32(height + 1), bytes32(32 * b"1"), None)
|
|
271
|
++
)
|
|
272
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
273
|
++
|
|
274
|
++
|
|
275
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
276
|
++
@pytest.mark.anyio
|
|
277
|
++
async def test_cat_reuse_address(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
278
|
++
num_blocks = 3
|
|
279
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
280
|
++
full_node_api = full_nodes[0]
|
|
281
|
++
full_node_server = full_node_api.server
|
|
282
|
++
wallet_node, server_2 = wallets[0]
|
|
283
|
++
wallet_node_2, server_3 = wallets[1]
|
|
284
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
285
|
++
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
286
|
++
|
|
287
|
++
ph = await wallet.get_new_puzzlehash()
|
|
288
|
++
if trusted:
|
|
289
|
++
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
290
|
++
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
291
|
++
else:
|
|
292
|
++
wallet_node.config["trusted_peers"] = {}
|
|
293
|
++
wallet_node_2.config["trusted_peers"] = {}
|
|
294
|
++
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
295
|
++
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
296
|
++
|
|
297
|
++
for _ in range(num_blocks):
|
|
298
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
299
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
300
|
++
|
|
301
|
++
funds = sum(
|
|
302
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
303
|
++
)
|
|
346
304
|
|
|
347
|
--
|
|
305
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
348
306
|
|
|
349
|
--
|
|
350
|
--
|
|
351
|
--
|
|
307
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
308
|
++
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
309
|
++
wallet_node.wallet_state_manager,
|
|
310
|
++
wallet,
|
|
311
|
++
{"identifier": "genesis_by_id"},
|
|
312
|
++
uint64(100),
|
|
313
|
++
DEFAULT_TX_CONFIG,
|
|
352
314
|
)
|
|
353
|
--
|
|
354
|
--
|
|
355
|
--
|
|
356
|
--
assert tx_record.to_puzzle_hash == cat_2_hash
|
|
357
|
--
assert len(tx_record.spend_bundle.coin_spends) == 2
|
|
358
|
--
for cs in tx_record.spend_bundle.coin_spends:
|
|
359
|
--
if cs.coin.amount == 100:
|
|
360
|
--
old_puzhash = cs.coin.puzzle_hash.hex()
|
|
361
|
--
new_puzhash = [c.puzzle_hash.hex() for c in tx_record.additions]
|
|
362
|
--
assert old_puzhash in new_puzhash
|
|
315
|
++
tx_queue = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
316
|
++
tx_record = tx_queue[0]
|
|
317
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
363
318
|
|
|
364
|
--
|
|
319
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
|
|
320
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
|
|
365
321
|
|
|
366
|
--
|
|
322
|
++
assert cat_wallet.cat_info.limitations_program_hash is not None
|
|
323
|
++
asset_id = cat_wallet.get_asset_id()
|
|
367
324
|
|
|
368
|
--
|
|
369
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
|
|
325
|
++
cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet2, asset_id)
|
|
370
326
|
|
|
371
|
--
|
|
327
|
++
assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
|
|
372
328
|
|
|
373
|
--
|
|
374
|
--
|
|
329
|
++
cat_2_hash = await cat_wallet_2.get_new_inner_hash()
|
|
330
|
++
tx_records = await cat_wallet.generate_signed_transaction(
|
|
331
|
++
[uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG.override(reuse_puzhash=True), fee=uint64(1)
|
|
332
|
++
)
|
|
333
|
++
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
334
|
++
for tx_record in tx_records:
|
|
335
|
++
if tx_record.wallet_id is cat_wallet.id():
|
|
336
|
++
assert tx_record.to_puzzle_hash == cat_2_hash
|
|
337
|
++
assert tx_record.spend_bundle is not None
|
|
338
|
++
assert len(tx_record.spend_bundle.coin_spends) == 2
|
|
339
|
++
for cs in tx_record.spend_bundle.coin_spends:
|
|
340
|
++
if cs.coin.amount == 100:
|
|
341
|
++
old_puzhash = cs.coin.puzzle_hash.hex()
|
|
342
|
++
new_puzhash = [c.puzzle_hash.hex() for c in tx_record.additions]
|
|
343
|
++
assert old_puzhash in new_puzhash
|
|
375
344
|
|
|
376
|
--
|
|
377
|
--
await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
345
|
++
await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
|
|
378
346
|
|
|
379
|
--
|
|
380
|
--
tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
|
|
381
|
--
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
347
|
++
await time_out_assert(20, cat_wallet.get_pending_change_balance, 40)
|
|
382
348
|
|
|
383
|
--
|
|
349
|
++
for _ in range(1, num_blocks):
|
|
350
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"\0")))
|
|
384
351
|
|
|
385
|
--
|
|
352
|
++
await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
|
|
353
|
++
|
|
354
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
355
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
|
|
356
|
++
|
|
357
|
++
await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 60)
|
|
358
|
++
await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
386
359
|
|
|
387
|
--
|
|
388
|
--
|
|
360
|
++
cat_hash = await cat_wallet.get_new_inner_hash()
|
|
361
|
++
tx_records = await cat_wallet_2.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
|
|
362
|
++
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
389
363
|
|
|
390
|
--
|
|
391
|
--
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(height - 1, height + 1, 32 * b"1", None))
|
|
392
|
--
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
364
|
++
await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records)
|
|
393
365
|
|
|
394
|
--
|
|
395
|
--
|
|
396
|
--
|
|
366
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
367
|
++
|
|
368
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 55)
|
|
369
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 55)
|
|
370
|
++
|
|
371
|
++
height = full_node_api.full_node.blockchain.get_peak_height()
|
|
372
|
++
assert height is not None
|
|
373
|
++
await full_node_api.reorg_from_index_to_new_index(
|
|
374
|
++
ReorgProtocol(uint32(height - 1), uint32(height + 1), bytes32(32 * b"1"), None)
|
|
397
375
|
)
|
|
398
|
--
|
|
399
|
--
|
|
400
|
--
num_blocks = 3
|
|
401
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
402
|
--
full_node_api = full_nodes[0]
|
|
403
|
--
full_node_server = full_node_api.server
|
|
404
|
--
wallet_node, server_2 = wallets[0]
|
|
405
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
406
|
--
|
|
407
|
--
ph = await wallet.get_new_puzzlehash()
|
|
408
|
--
if trusted:
|
|
409
|
--
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
410
|
--
else:
|
|
411
|
--
wallet_node.config["trusted_peers"] = {}
|
|
412
|
--
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
413
|
--
|
|
414
|
--
for i in range(0, num_blocks):
|
|
415
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
416
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
|
|
417
|
--
|
|
418
|
--
funds = sum(
|
|
419
|
--
[
|
|
420
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
421
|
--
for i in range(1, num_blocks + 1)
|
|
422
|
--
]
|
|
423
|
--
)
|
|
376
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
377
|
++
|
|
424
378
|
|
|
425
|
--
|
|
426
|
--
|
|
427
|
--
|
|
428
|
--
|
|
429
|
--
|
|
430
|
--
|
|
431
|
--
|
|
432
|
--
|
|
433
|
--
|
|
434
|
--
|
|
435
|
--
|
|
436
|
--
|
|
437
|
--
|
|
438
|
--
|
|
439
|
--
|
|
440
|
--
|
|
441
|
--
|
|
442
|
--
|
|
443
|
--
|
|
444
|
--
|
|
445
|
--
|
|
446
|
--
|
|
447
|
--
|
|
448
|
--
|
|
449
|
--
|
|
450
|
--
|
|
451
|
--
@pytest.mark.parametrize(
|
|
452
|
--
"trusted",
|
|
453
|
--
[True, False],
|
|
379
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
380
|
++
@pytest.mark.anyio
|
|
381
|
++
async def test_get_wallet_for_asset_id(
|
|
382
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
383
|
++
) -> None:
|
|
384
|
++
num_blocks = 3
|
|
385
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
386
|
++
full_node_api = full_nodes[0]
|
|
387
|
++
full_node_server = full_node_api.server
|
|
388
|
++
wallet_node, server_2 = wallets[0]
|
|
389
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
390
|
++
|
|
391
|
++
ph = await wallet.get_new_puzzlehash()
|
|
392
|
++
if trusted:
|
|
393
|
++
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
394
|
++
else:
|
|
395
|
++
wallet_node.config["trusted_peers"] = {}
|
|
396
|
++
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
397
|
++
|
|
398
|
++
for _ in range(num_blocks):
|
|
399
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
400
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
401
|
++
|
|
402
|
++
funds = sum(
|
|
403
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
454
404
|
)
|
|
455
|
--
|
|
456
|
--
|
|
457
|
--
|
|
458
|
--
|
|
459
|
--
|
|
460
|
--
|
|
461
|
--
|
|
462
|
--
|
|
463
|
--
|
|
464
|
--
|
|
465
|
--
|
|
466
|
--
ph = await wallet.get_new_puzzlehash()
|
|
467
|
--
if trusted:
|
|
468
|
--
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
469
|
--
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
470
|
--
else:
|
|
471
|
--
wallet_node.config["trusted_peers"] = {}
|
|
472
|
--
wallet_node_2.config["trusted_peers"] = {}
|
|
473
|
--
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
474
|
--
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
475
|
--
|
|
476
|
--
for i in range(0, num_blocks):
|
|
477
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
478
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
|
|
479
|
--
|
|
480
|
--
funds = sum(
|
|
481
|
--
[
|
|
482
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
483
|
--
for i in range(1, num_blocks + 1)
|
|
484
|
--
]
|
|
405
|
++
|
|
406
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
407
|
++
|
|
408
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
409
|
++
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
410
|
++
wallet_node.wallet_state_manager,
|
|
411
|
++
wallet,
|
|
412
|
++
{"identifier": "genesis_by_id"},
|
|
413
|
++
uint64(100),
|
|
414
|
++
DEFAULT_TX_CONFIG,
|
|
485
415
|
)
|
|
486
416
|
|
|
487
|
--
|
|
417
|
++
for _ in range(1, num_blocks):
|
|
418
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
488
419
|
|
|
489
|
--
|
|
490
|
--
|
|
491
|
--
|
|
492
|
--
|
|
493
|
--
{"identifier": "genesis_by_id"},
|
|
494
|
--
uint64(100),
|
|
495
|
--
DEFAULT_TX_CONFIG,
|
|
496
|
--
)
|
|
497
|
--
tx_records: List[TransactionRecord] = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
498
|
--
await full_node_api.process_transaction_records(records=tx_records)
|
|
420
|
++
asset_id = cat_wallet.get_asset_id()
|
|
421
|
++
assert cat_wallet.cat_info.my_tail is not None
|
|
422
|
++
await cat_wallet.set_tail_program(bytes(cat_wallet.cat_info.my_tail).hex())
|
|
423
|
++
assert await wallet_node.wallet_state_manager.get_wallet_for_asset_id(asset_id) == cat_wallet
|
|
499
424
|
|
|
500
|
--
|
|
501
|
--
|
|
425
|
++
# Test that the a default CAT will initialize correctly
|
|
426
|
++
asset = DEFAULT_CATS[next(iter(DEFAULT_CATS))]
|
|
427
|
++
asset_id = asset["asset_id"]
|
|
428
|
++
cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node.wallet_state_manager, wallet, asset_id)
|
|
429
|
++
assert cat_wallet_2.get_name() == asset["name"]
|
|
430
|
++
await cat_wallet_2.set_name("Test Name")
|
|
431
|
++
assert cat_wallet_2.get_name() == "Test Name"
|
|
502
432
|
|
|
503
|
--
assert cat_wallet.cat_info.limitations_program_hash is not None
|
|
504
|
--
asset_id = cat_wallet.get_asset_id()
|
|
505
433
|
|
|
506
|
--
|
|
507
|
--
|
|
508
|
--
|
|
434
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
435
|
++
@pytest.mark.anyio
|
|
436
|
++
async def test_cat_doesnt_see_eve(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
437
|
++
num_blocks = 3
|
|
438
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
439
|
++
full_node_api = full_nodes[0]
|
|
440
|
++
full_node_server = full_node_api.server
|
|
441
|
++
wallet_node, server_2 = wallets[0]
|
|
442
|
++
wallet_node_2, server_3 = wallets[1]
|
|
443
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
444
|
++
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
445
|
++
|
|
446
|
++
ph = await wallet.get_new_puzzlehash()
|
|
447
|
++
if trusted:
|
|
448
|
++
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
449
|
++
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
450
|
++
else:
|
|
451
|
++
wallet_node.config["trusted_peers"] = {}
|
|
452
|
++
wallet_node_2.config["trusted_peers"] = {}
|
|
453
|
++
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
454
|
++
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
455
|
++
|
|
456
|
++
for _ in range(num_blocks):
|
|
457
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
458
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
509
459
|
|
|
510
|
--
|
|
460
|
++
funds = sum(
|
|
461
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
462
|
++
)
|
|
463
|
++
|
|
464
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
511
465
|
|
|
512
|
--
|
|
513
|
--
|
|
514
|
--
|
|
466
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
467
|
++
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
468
|
++
wallet_node.wallet_state_manager,
|
|
469
|
++
wallet,
|
|
470
|
++
{"identifier": "genesis_by_id"},
|
|
471
|
++
uint64(100),
|
|
472
|
++
DEFAULT_TX_CONFIG,
|
|
515
473
|
)
|
|
516
|
--
|
|
517
|
--
|
|
474
|
++
tx_records = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
475
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
518
476
|
|
|
519
|
--
|
|
520
|
--
|
|
477
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
|
|
478
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
|
|
521
479
|
|
|
522
|
--
|
|
523
|
--
|
|
480
|
++
assert cat_wallet.cat_info.limitations_program_hash is not None
|
|
481
|
++
asset_id = cat_wallet.get_asset_id()
|
|
524
482
|
|
|
525
|
--
|
|
526
|
--
await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
483
|
++
cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet2, asset_id)
|
|
527
484
|
|
|
528
|
--
|
|
529
|
--
[tx_record] = await wallet.wallet_state_manager.main_wallet.generate_signed_transaction(
|
|
530
|
--
10, cc2_ph, DEFAULT_TX_CONFIG, 0
|
|
531
|
--
)
|
|
532
|
--
await wallet.wallet_state_manager.add_pending_transactions([tx_record])
|
|
533
|
--
await full_node_api.process_transaction_records(records=[tx_record])
|
|
485
|
++
assert cat_wallet.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
|
|
534
486
|
|
|
535
|
--
|
|
536
|
--
|
|
487
|
++
cat_2_hash = await cat_wallet_2.get_new_inner_hash()
|
|
488
|
++
tx_records = await cat_wallet.generate_signed_transaction(
|
|
489
|
++
[uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, fee=uint64(1)
|
|
490
|
++
)
|
|
491
|
++
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
492
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
493
|
++
|
|
494
|
++
await time_out_assert(30, wallet.get_confirmed_balance, funds - 101)
|
|
495
|
++
await time_out_assert(30, wallet.get_unconfirmed_balance, funds - 101)
|
|
537
496
|
|
|
538
|
--
|
|
539
|
--
|
|
540
|
--
return len(list(filter(lambda tx: tx.amount == 10, all_txs)))
|
|
497
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
498
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
|
|
541
499
|
|
|
542
|
--
|
|
543
|
--
|
|
544
|
--
await time_out_assert(20, cat_wallet_2.get_confirmed_balance, 60)
|
|
545
|
--
await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
500
|
++
await time_out_assert(20, cat_wallet_2.get_confirmed_balance, 60)
|
|
501
|
++
await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
546
502
|
|
|
547
|
--
|
|
548
|
--
|
|
549
|
--
|
|
503
|
++
cc2_ph = await cat_wallet_2.get_new_cat_puzzle_hash()
|
|
504
|
++
[tx_record] = await wallet.wallet_state_manager.main_wallet.generate_signed_transaction(
|
|
505
|
++
uint64(10), cc2_ph, DEFAULT_TX_CONFIG
|
|
550
506
|
)
|
|
551
|
--
|
|
552
|
--
|
|
553
|
--
num_blocks = 3
|
|
554
|
--
full_nodes, wallets, _ = three_wallet_nodes
|
|
555
|
--
full_node_api = full_nodes[0]
|
|
556
|
--
full_node_server = full_node_api.server
|
|
557
|
--
wallet_node_0, wallet_server_0 = wallets[0]
|
|
558
|
--
wallet_node_1, wallet_server_1 = wallets[1]
|
|
559
|
--
wallet_node_2, wallet_server_2 = wallets[2]
|
|
560
|
--
wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
|
|
561
|
--
wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
|
|
562
|
--
wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
563
|
--
|
|
564
|
--
ph = await wallet_0.get_new_puzzlehash()
|
|
565
|
--
if trusted:
|
|
566
|
--
wallet_node_0.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
567
|
--
wallet_node_1.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
568
|
--
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
569
|
--
else:
|
|
570
|
--
wallet_node_0.config["trusted_peers"] = {}
|
|
571
|
--
wallet_node_1.config["trusted_peers"] = {}
|
|
572
|
--
wallet_node_2.config["trusted_peers"] = {}
|
|
573
|
--
await wallet_server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
574
|
--
await wallet_server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
575
|
--
await wallet_server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
576
|
--
|
|
577
|
--
for i in range(0, num_blocks):
|
|
578
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
579
|
--
|
|
580
|
--
funds = sum(
|
|
581
|
--
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
|
|
582
|
--
)
|
|
507
|
++
await wallet.wallet_state_manager.add_pending_transactions([tx_record])
|
|
508
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
583
509
|
|
|
584
|
--
|
|
510
|
++
id = cat_wallet_2.id()
|
|
511
|
++
wsm = cat_wallet_2.wallet_state_manager
|
|
585
512
|
|
|
586
|
--
|
|
587
|
--
|
|
588
|
--
|
|
589
|
--
wallet_0,
|
|
590
|
--
{"identifier": "genesis_by_id"},
|
|
591
|
--
uint64(100),
|
|
592
|
--
DEFAULT_TX_CONFIG,
|
|
593
|
--
)
|
|
594
|
--
tx_records: List[TransactionRecord] = await wallet_node_0.wallet_state_manager.tx_store.get_not_sent()
|
|
595
|
--
await full_node_api.process_transaction_records(records=tx_records)
|
|
513
|
++
async def query_and_assert_transactions(wsm: WalletStateManager, id: uint32) -> int:
|
|
514
|
++
all_txs = await wsm.tx_store.get_all_transactions_for_wallet(id)
|
|
515
|
++
return len(list(filter(lambda tx: tx.amount == 10, all_txs)))
|
|
596
516
|
|
|
597
|
--
|
|
598
|
--
|
|
517
|
++
await time_out_assert(20, query_and_assert_transactions, 0, wsm, id)
|
|
518
|
++
await time_out_assert(20, wsm.get_confirmed_balance_for_wallet, 60, id)
|
|
519
|
++
await time_out_assert(20, cat_wallet_2.get_confirmed_balance, 60)
|
|
520
|
++
await time_out_assert(20, cat_wallet_2.get_unconfirmed_balance, 60)
|
|
599
521
|
|
|
600
|
--
assert cat_wallet_0.cat_info.limitations_program_hash is not None
|
|
601
|
--
asset_id = cat_wallet_0.get_asset_id()
|
|
602
522
|
|
|
603
|
--
|
|
604
|
--
|
|
605
|
--
|
|
523
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
524
|
++
@pytest.mark.anyio
|
|
525
|
++
async def test_cat_spend_multiple(
|
|
526
|
++
self_hostname: str, three_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
527
|
++
) -> None:
|
|
528
|
++
num_blocks = 3
|
|
529
|
++
full_nodes, wallets, _ = three_wallet_nodes
|
|
530
|
++
full_node_api = full_nodes[0]
|
|
531
|
++
full_node_server = full_node_api.server
|
|
532
|
++
wallet_node_0, wallet_server_0 = wallets[0]
|
|
533
|
++
wallet_node_1, wallet_server_1 = wallets[1]
|
|
534
|
++
wallet_node_2, wallet_server_2 = wallets[2]
|
|
535
|
++
wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
|
|
536
|
++
wallet_1 = wallet_node_1.wallet_state_manager.main_wallet
|
|
537
|
++
wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
538
|
++
|
|
539
|
++
ph = await wallet_0.get_new_puzzlehash()
|
|
540
|
++
if trusted:
|
|
541
|
++
wallet_node_0.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
542
|
++
wallet_node_1.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
543
|
++
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
544
|
++
else:
|
|
545
|
++
wallet_node_0.config["trusted_peers"] = {}
|
|
546
|
++
wallet_node_1.config["trusted_peers"] = {}
|
|
547
|
++
wallet_node_2.config["trusted_peers"] = {}
|
|
548
|
++
await wallet_server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
549
|
++
await wallet_server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
550
|
++
await wallet_server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
551
|
++
|
|
552
|
++
for _ in range(num_blocks):
|
|
553
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
554
|
++
|
|
555
|
++
funds = sum(
|
|
556
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
|
|
557
|
++
)
|
|
606
558
|
|
|
607
|
--
|
|
608
|
--
|
|
559
|
++
await time_out_assert(20, wallet_0.get_confirmed_balance, funds)
|
|
560
|
++
|
|
561
|
++
async with wallet_node_0.wallet_state_manager.lock:
|
|
562
|
++
cat_wallet_0, _ = await CATWallet.create_new_cat_wallet(
|
|
563
|
++
wallet_node_0.wallet_state_manager,
|
|
564
|
++
wallet_0,
|
|
565
|
++
{"identifier": "genesis_by_id"},
|
|
566
|
++
uint64(100),
|
|
567
|
++
DEFAULT_TX_CONFIG,
|
|
609
568
|
)
|
|
569
|
++
tx_records = await wallet_node_0.wallet_state_manager.tx_store.get_not_sent()
|
|
570
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
610
571
|
|
|
611
|
--
|
|
612
|
--
|
|
572
|
++
await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 100)
|
|
573
|
++
await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 100)
|
|
613
574
|
|
|
614
|
--
|
|
615
|
--
|
|
575
|
++
assert cat_wallet_0.cat_info.limitations_program_hash is not None
|
|
576
|
++
asset_id = cat_wallet_0.get_asset_id()
|
|
616
577
|
|
|
617
|
--
|
|
618
|
--
[uint64(60), uint64(20)], [cat_1_hash, cat_2_hash], DEFAULT_TX_CONFIG
|
|
619
|
--
)
|
|
620
|
--
await wallet_0.wallet_state_manager.add_pending_transactions(tx_records)
|
|
621
|
--
await full_node_api.process_transaction_records(records=tx_records)
|
|
578
|
++
cat_wallet_1 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_1.wallet_state_manager, wallet_1, asset_id)
|
|
622
579
|
|
|
623
|
--
|
|
624
|
--
await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 20)
|
|
580
|
++
cat_wallet_2 = await CATWallet.get_or_create_wallet_for_cat(wallet_node_2.wallet_state_manager, wallet_2, asset_id)
|
|
625
581
|
|
|
626
|
--
|
|
627
|
--
|
|
582
|
++
assert cat_wallet_0.cat_info.limitations_program_hash == cat_wallet_1.cat_info.limitations_program_hash
|
|
583
|
++
assert cat_wallet_0.cat_info.limitations_program_hash == cat_wallet_2.cat_info.limitations_program_hash
|
|
628
584
|
|
|
629
|
--
|
|
630
|
--
|
|
585
|
++
cat_1_hash = await cat_wallet_1.get_new_inner_hash()
|
|
586
|
++
cat_2_hash = await cat_wallet_2.get_new_inner_hash()
|
|
631
587
|
|
|
632
|
--
|
|
588
|
++
tx_records = await cat_wallet_0.generate_signed_transaction(
|
|
589
|
++
[uint64(60), uint64(20)], [cat_1_hash, cat_2_hash], DEFAULT_TX_CONFIG
|
|
590
|
++
)
|
|
591
|
++
await wallet_0.wallet_state_manager.add_pending_transactions(tx_records)
|
|
592
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
633
593
|
|
|
634
|
--
|
|
635
|
--
|
|
594
|
++
await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 20)
|
|
595
|
++
await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 20)
|
|
636
596
|
|
|
637
|
--
|
|
638
|
--
|
|
597
|
++
await time_out_assert(30, cat_wallet_1.get_confirmed_balance, 60)
|
|
598
|
++
await time_out_assert(30, cat_wallet_1.get_unconfirmed_balance, 60)
|
|
639
599
|
|
|
640
|
--
|
|
600
|
++
await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 20)
|
|
601
|
++
await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 20)
|
|
641
602
|
|
|
642
|
--
|
|
643
|
--
await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 55)
|
|
603
|
++
cat_hash = await cat_wallet_0.get_new_inner_hash()
|
|
644
604
|
|
|
645
|
--
|
|
646
|
--
|
|
605
|
++
tx_records = await cat_wallet_1.generate_signed_transaction([uint64(15)], [cat_hash], DEFAULT_TX_CONFIG)
|
|
606
|
++
await wallet_1.wallet_state_manager.add_pending_transactions(tx_records)
|
|
647
607
|
|
|
648
|
--
|
|
649
|
--
|
|
608
|
++
tx_records_2 = await cat_wallet_2.generate_signed_transaction([uint64(20)], [cat_hash], DEFAULT_TX_CONFIG)
|
|
609
|
++
await wallet_2.wallet_state_manager.add_pending_transactions(tx_records_2)
|
|
650
610
|
|
|
651
|
--
|
|
652
|
--
|
|
653
|
--
|
|
654
|
--
|
|
655
|
--
|
|
656
|
--
|
|
657
|
--
|
|
658
|
--
|
|
659
|
--
|
|
660
|
--
|
|
661
|
--
|
|
662
|
--
|
|
663
|
--
|
|
664
|
--
|
|
665
|
--
|
|
666
|
--
|
|
667
|
--
|
|
668
|
--
|
|
669
|
--
|
|
670
|
--
|
|
671
|
--
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
|
672
|
--
@pytest.mark.parametrize("trusted", [True, False])
|
|
673
|
--
@pytest.mark.anyio
|
|
674
|
--
async def test_cat_max_amount_send(self, self_hostname, two_wallet_nodes, trusted):
|
|
675
|
--
num_blocks = 3
|
|
676
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
677
|
--
full_node_api = full_nodes[0]
|
|
678
|
--
full_node_server = full_node_api.server
|
|
679
|
--
wallet_node, server_2 = wallets[0]
|
|
680
|
--
wallet_node_2, server_3 = wallets[1]
|
|
681
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
682
|
--
|
|
683
|
--
ph = await wallet.get_new_puzzlehash()
|
|
684
|
--
if trusted:
|
|
685
|
--
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
686
|
--
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
687
|
--
else:
|
|
688
|
--
wallet_node.config["trusted_peers"] = {}
|
|
689
|
--
wallet_node_2.config["trusted_peers"] = {}
|
|
690
|
--
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
691
|
--
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
692
|
--
|
|
693
|
--
for i in range(0, num_blocks):
|
|
694
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
695
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
|
|
696
|
--
|
|
697
|
--
funds = sum(
|
|
698
|
--
[
|
|
699
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
700
|
--
for i in range(1, num_blocks + 1)
|
|
701
|
--
]
|
|
611
|
++
await full_node_api.process_transaction_records(records=[*tx_records, *tx_records_2])
|
|
612
|
++
|
|
613
|
++
await time_out_assert(20, cat_wallet_0.get_confirmed_balance, 55)
|
|
614
|
++
await time_out_assert(20, cat_wallet_0.get_unconfirmed_balance, 55)
|
|
615
|
++
|
|
616
|
++
await time_out_assert(30, cat_wallet_1.get_confirmed_balance, 45)
|
|
617
|
++
await time_out_assert(30, cat_wallet_1.get_unconfirmed_balance, 45)
|
|
618
|
++
|
|
619
|
++
await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 0)
|
|
620
|
++
await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 0)
|
|
621
|
++
|
|
622
|
++
txs = await wallet_1.wallet_state_manager.tx_store.get_transactions_between(cat_wallet_1.id(), 0, 100000)
|
|
623
|
++
# Test with Memo
|
|
624
|
++
tx_records_3 = await cat_wallet_1.generate_signed_transaction(
|
|
625
|
++
[uint64(30)], [cat_hash], DEFAULT_TX_CONFIG, memos=[[b"Markus Walburg"]]
|
|
626
|
++
)
|
|
627
|
++
with pytest.raises(ValueError):
|
|
628
|
++
await cat_wallet_1.generate_signed_transaction(
|
|
629
|
++
[uint64(30)], [cat_hash], DEFAULT_TX_CONFIG, memos=[[b"too"], [b"many"], [b"memos"]]
|
|
702
630
|
)
|
|
703
631
|
|
|
704
|
--
|
|
632
|
++
await wallet_1.wallet_state_manager.add_pending_transactions(tx_records_3)
|
|
633
|
++
await time_out_assert(15, full_node_api.txs_in_mempool, True, tx_records_3)
|
|
634
|
++
txs = await wallet_1.wallet_state_manager.tx_store.get_transactions_between(cat_wallet_1.id(), 0, 100000)
|
|
635
|
++
for tx in txs:
|
|
636
|
++
if tx.amount == 30:
|
|
637
|
++
memos = tx.get_memos()
|
|
638
|
++
assert len(memos) == 2 # One for tx, one for change
|
|
639
|
++
assert b"Markus Walburg" in [v for v_list in memos.values() for v in v_list]
|
|
640
|
++
assert tx.spend_bundle is not None
|
|
641
|
++
assert list(memos.keys())[0] in [a.name() for a in tx.spend_bundle.additions()]
|
|
642
|
++
|
|
705
643
|
|
|
706
|
--
|
|
707
|
--
|
|
708
|
--
|
|
709
|
--
|
|
710
|
--
|
|
711
|
--
|
|
712
|
--
|
|
713
|
--
|
|
714
|
--
|
|
715
|
--
|
|
644
|
++
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
|
645
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
646
|
++
@pytest.mark.anyio
|
|
647
|
++
async def test_cat_max_amount_send(
|
|
648
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
649
|
++
) -> None:
|
|
650
|
++
num_blocks = 3
|
|
651
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
652
|
++
full_node_api = full_nodes[0]
|
|
653
|
++
full_node_server = full_node_api.server
|
|
654
|
++
wallet_node, server_2 = wallets[0]
|
|
655
|
++
wallet_node_2, server_3 = wallets[1]
|
|
656
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
657
|
++
|
|
658
|
++
ph = await wallet.get_new_puzzlehash()
|
|
659
|
++
if trusted:
|
|
660
|
++
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
661
|
++
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
662
|
++
else:
|
|
663
|
++
wallet_node.config["trusted_peers"] = {}
|
|
664
|
++
wallet_node_2.config["trusted_peers"] = {}
|
|
665
|
++
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
666
|
++
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
667
|
++
|
|
668
|
++
for _ in range(num_blocks):
|
|
669
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
670
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
716
671
|
|
|
717
|
--
|
|
718
|
--
|
|
672
|
++
funds = sum(
|
|
673
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
674
|
++
)
|
|
719
675
|
|
|
720
|
--
|
|
676
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
721
677
|
|
|
722
|
--
|
|
723
|
--
|
|
724
|
--
|
|
725
|
--
|
|
726
|
--
|
|
727
|
--
|
|
728
|
--
puzzle_hashes.append(cat_2_hash)
|
|
729
|
--
spent_coint = (await cat_wallet.get_cat_spendable_coins())[0].coin
|
|
730
|
--
tx_records = await cat_wallet.generate_signed_transaction(
|
|
731
|
--
amounts, puzzle_hashes, DEFAULT_TX_CONFIG, coins={spent_coint}
|
|
732
|
--
)
|
|
733
|
--
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
734
|
--
await full_node_api.process_transaction_records(records=tx_records)
|
|
735
|
--
|
|
736
|
--
await asyncio.sleep(2)
|
|
737
|
--
|
|
738
|
--
async def check_all_there():
|
|
739
|
--
spendable = await cat_wallet.get_cat_spendable_coins()
|
|
740
|
--
spendable_name_set = set()
|
|
741
|
--
for record in spendable:
|
|
742
|
--
spendable_name_set.add(record.coin.name())
|
|
743
|
--
puzzle_hash = construct_cat_puzzle(
|
|
744
|
--
CAT_MOD, cat_wallet.cat_info.limitations_program_hash, cat_2
|
|
745
|
--
).get_tree_hash()
|
|
746
|
--
for i in range(1, 50):
|
|
747
|
--
coin = Coin(spent_coint.name(), puzzle_hash, i)
|
|
748
|
--
if coin.name() not in spendable_name_set:
|
|
749
|
--
return False
|
|
750
|
--
return True
|
|
751
|
--
|
|
752
|
--
await time_out_assert(20, check_all_there, True)
|
|
753
|
--
await asyncio.sleep(5)
|
|
754
|
--
max_sent_amount = await cat_wallet.get_max_send_amount()
|
|
755
|
--
|
|
756
|
--
# 1) Generate transaction that is under the limit
|
|
757
|
--
[transaction_record] = await cat_wallet.generate_signed_transaction(
|
|
758
|
--
[max_sent_amount - 1],
|
|
759
|
--
[ph],
|
|
678
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
679
|
++
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
680
|
++
wallet_node.wallet_state_manager,
|
|
681
|
++
wallet,
|
|
682
|
++
{"identifier": "genesis_by_id"},
|
|
683
|
++
uint64(100000),
|
|
760
684
|
DEFAULT_TX_CONFIG,
|
|
761
685
|
)
|
|
686
|
++
tx_records = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
687
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
762
688
|
|
|
763
|
--
|
|
689
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 100000)
|
|
690
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100000)
|
|
764
691
|
|
|
765
|
--
|
|
766
|
--
[transaction_record] = await cat_wallet.generate_signed_transaction(
|
|
767
|
--
[max_sent_amount],
|
|
768
|
--
[ph],
|
|
769
|
--
DEFAULT_TX_CONFIG,
|
|
770
|
--
)
|
|
692
|
++
assert cat_wallet.cat_info.limitations_program_hash is not None
|
|
771
693
|
|
|
772
|
--
|
|
773
|
--
|
|
774
|
--
|
|
775
|
--
|
|
776
|
--
|
|
777
|
--
|
|
778
|
--
|
|
779
|
--
|
|
780
|
--
|
|
781
|
--
|
|
782
|
--
|
|
783
|
--
|
|
784
|
--
|
|
785
|
--
@pytest.mark.anyio
|
|
786
|
--
async def test_cat_hint(self, self_hostname, two_wallet_nodes, trusted, autodiscovery):
|
|
787
|
--
num_blocks = 3
|
|
788
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
789
|
--
full_node_api = full_nodes[0]
|
|
790
|
--
full_node_server = full_node_api.server
|
|
791
|
--
wallet_node, server_2 = wallets[0]
|
|
792
|
--
wallet_node_2, server_3 = wallets[1]
|
|
793
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
794
|
--
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
795
|
--
|
|
796
|
--
ph = await wallet.get_new_puzzlehash()
|
|
797
|
--
if trusted:
|
|
798
|
--
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
799
|
--
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
800
|
--
else:
|
|
801
|
--
wallet_node.config["trusted_peers"] = {}
|
|
802
|
--
wallet_node_2.config["trusted_peers"] = {}
|
|
803
|
--
wallet_node.config["automatically_add_unknown_cats"] = autodiscovery
|
|
804
|
--
wallet_node_2.config["automatically_add_unknown_cats"] = autodiscovery
|
|
805
|
--
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
806
|
--
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
807
|
--
|
|
808
|
--
for i in range(0, num_blocks):
|
|
809
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
810
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))
|
|
811
|
--
|
|
812
|
--
funds = sum(
|
|
813
|
--
[
|
|
814
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
815
|
--
for i in range(1, num_blocks + 1)
|
|
816
|
--
]
|
|
817
|
--
)
|
|
694
|
++
cat_2 = await cat_wallet.get_new_inner_puzzle()
|
|
695
|
++
cat_2_hash = cat_2.get_tree_hash()
|
|
696
|
++
amounts = []
|
|
697
|
++
puzzle_hashes = []
|
|
698
|
++
for i in range(1, 50):
|
|
699
|
++
amounts.append(uint64(i))
|
|
700
|
++
puzzle_hashes.append(cat_2_hash)
|
|
701
|
++
spent_coint = (await cat_wallet.get_cat_spendable_coins())[0].coin
|
|
702
|
++
tx_records = await cat_wallet.generate_signed_transaction(
|
|
703
|
++
amounts, puzzle_hashes, DEFAULT_TX_CONFIG, coins={spent_coint}
|
|
704
|
++
)
|
|
705
|
++
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
706
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
818
707
|
|
|
819
|
--
|
|
820
|
--
|
|
821
|
--
|
|
822
|
--
|
|
823
|
--
|
|
824
|
--
|
|
825
|
--
|
|
826
|
--
|
|
827
|
--
|
|
828
|
--
)
|
|
829
|
--
|
|
830
|
--
|
|
831
|
--
|
|
832
|
--
|
|
833
|
--
|
|
834
|
--
|
|
835
|
--
|
|
836
|
--
|
|
837
|
--
|
|
838
|
--
|
|
839
|
--
)
|
|
708
|
++
await asyncio.sleep(2)
|
|
709
|
++
|
|
710
|
++
async def check_all_there() -> bool:
|
|
711
|
++
spendable = await cat_wallet.get_cat_spendable_coins()
|
|
712
|
++
spendable_name_set = set()
|
|
713
|
++
for record in spendable:
|
|
714
|
++
spendable_name_set.add(record.coin.name())
|
|
715
|
++
puzzle_hash = construct_cat_puzzle(CAT_MOD, cat_wallet.cat_info.limitations_program_hash, cat_2).get_tree_hash()
|
|
716
|
++
for i in range(1, 50):
|
|
717
|
++
coin = Coin(spent_coint.name(), puzzle_hash, i)
|
|
718
|
++
if coin.name() not in spendable_name_set:
|
|
719
|
++
return False
|
|
720
|
++
return True
|
|
721
|
++
|
|
722
|
++
await time_out_assert(20, check_all_there, True)
|
|
723
|
++
await asyncio.sleep(5)
|
|
724
|
++
max_sent_amount = await cat_wallet.get_max_send_amount()
|
|
725
|
++
|
|
726
|
++
# 1) Generate transaction that is under the limit
|
|
727
|
++
[transaction_record] = await cat_wallet.generate_signed_transaction(
|
|
728
|
++
[uint64(max_sent_amount - 1)], [ph], DEFAULT_TX_CONFIG
|
|
729
|
++
)
|
|
730
|
++
assert transaction_record.amount == uint64(max_sent_amount - 1)
|
|
840
731
|
|
|
841
|
--
|
|
732
|
++
# 2) Generate transaction that is equal to limit
|
|
733
|
++
[transaction_record] = await cat_wallet.generate_signed_transaction(
|
|
734
|
++
[uint64(max_sent_amount)], [ph], DEFAULT_TX_CONFIG
|
|
735
|
++
)
|
|
736
|
++
assert transaction_record.amount == uint64(max_sent_amount)
|
|
737
|
++
|
|
738
|
++
# 3) Generate transaction that is greater than limit
|
|
739
|
++
with pytest.raises(ValueError):
|
|
740
|
++
await cat_wallet.generate_signed_transaction([uint64(max_sent_amount + 1)], [ph], DEFAULT_TX_CONFIG)
|
|
842
741
|
|
|
843
|
--
await full_node_api.process_transaction_records(records=tx_records)
|
|
844
742
|
|
|
845
|
--
|
|
846
|
--
|
|
743
|
++
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
|
744
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
745
|
++
@pytest.mark.parametrize("autodiscovery", [True, False])
|
|
746
|
++
@pytest.mark.anyio
|
|
747
|
++
async def test_cat_hint(
|
|
748
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool, autodiscovery: bool
|
|
749
|
++
) -> None:
|
|
750
|
++
num_blocks = 3
|
|
751
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
752
|
++
full_node_api = full_nodes[0]
|
|
753
|
++
full_node_server = full_node_api.server
|
|
754
|
++
wallet_node, server_2 = wallets[0]
|
|
755
|
++
wallet_node_2, server_3 = wallets[1]
|
|
756
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
757
|
++
wallet2 = wallet_node_2.wallet_state_manager.main_wallet
|
|
758
|
++
|
|
759
|
++
ph = await wallet.get_new_puzzlehash()
|
|
760
|
++
if trusted:
|
|
761
|
++
wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
762
|
++
wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
763
|
++
else:
|
|
764
|
++
wallet_node.config["trusted_peers"] = {}
|
|
765
|
++
wallet_node_2.config["trusted_peers"] = {}
|
|
766
|
++
wallet_node.config["automatically_add_unknown_cats"] = autodiscovery
|
|
767
|
++
wallet_node_2.config["automatically_add_unknown_cats"] = autodiscovery
|
|
768
|
++
await server_2.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
769
|
++
await server_3.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
770
|
++
|
|
771
|
++
for _ in range(num_blocks):
|
|
772
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
773
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
|
847
774
|
|
|
848
|
--
|
|
849
|
--
|
|
850
|
--
|
|
851
|
--
else:
|
|
852
|
--
# Autodiscovery disabled: test that no wallet was created
|
|
853
|
--
await time_out_assert(20, check_wallets, 1, wallet_node_2)
|
|
775
|
++
funds = sum(
|
|
776
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
777
|
++
)
|
|
854
778
|
|
|
855
|
--
|
|
856
|
--
wallet_node_2.wallet_state_manager.default_cats = {
|
|
857
|
--
cat_wallet.cat_info.limitations_program_hash.hex(): {
|
|
858
|
--
"asset_id": cat_wallet.cat_info.limitations_program_hash.hex(),
|
|
859
|
--
"name": "Test",
|
|
860
|
--
"symbol": "TST",
|
|
861
|
--
}
|
|
862
|
--
}
|
|
779
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
863
780
|
|
|
864
|
--
|
|
865
|
--
|
|
866
|
--
|
|
781
|
++
async with wallet_node.wallet_state_manager.lock:
|
|
782
|
++
cat_wallet, _ = await CATWallet.create_new_cat_wallet(
|
|
783
|
++
wallet_node.wallet_state_manager,
|
|
784
|
++
wallet,
|
|
785
|
++
{"identifier": "genesis_by_id"},
|
|
786
|
++
uint64(100),
|
|
787
|
++
DEFAULT_TX_CONFIG,
|
|
867
788
|
)
|
|
789
|
++
tx_records = await wallet_node.wallet_state_manager.tx_store.get_not_sent()
|
|
790
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
868
791
|
|
|
869
|
--
|
|
792
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 100)
|
|
793
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 100)
|
|
794
|
++
assert cat_wallet.cat_info.limitations_program_hash is not None
|
|
870
795
|
|
|
871
|
--
|
|
796
|
++
cat_2_hash = await wallet2.get_new_puzzlehash()
|
|
797
|
++
tx_records = await cat_wallet.generate_signed_transaction(
|
|
798
|
++
[uint64(60)], [cat_2_hash], DEFAULT_TX_CONFIG, memos=[[cat_2_hash]]
|
|
799
|
++
)
|
|
800
|
++
|
|
801
|
++
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
802
|
++
|
|
803
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
872
804
|
|
|
873
|
--
|
|
874
|
--
|
|
805
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 40)
|
|
806
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 40)
|
|
875
807
|
|
|
876
|
--
|
|
808
|
++
if autodiscovery:
|
|
809
|
++
# Autodiscovery enabled: test that wallet was created at this point
|
|
877
810
|
await time_out_assert(20, check_wallets, 2, wallet_node_2)
|
|
878
|
--
|
|
811
|
++
else:
|
|
812
|
++
# Autodiscovery disabled: test that no wallet was created
|
|
813
|
++
await time_out_assert(20, check_wallets, 1, wallet_node_2)
|
|
814
|
++
|
|
815
|
++
# Then we update the wallet's default CATs
|
|
816
|
++
wallet_node_2.wallet_state_manager.default_cats = {
|
|
817
|
++
cat_wallet.cat_info.limitations_program_hash.hex(): {
|
|
818
|
++
"asset_id": cat_wallet.cat_info.limitations_program_hash.hex(),
|
|
819
|
++
"name": "Test",
|
|
820
|
++
"symbol": "TST",
|
|
821
|
++
}
|
|
822
|
++
}
|
|
823
|
++
|
|
824
|
++
# Then we send another transaction
|
|
825
|
++
tx_records = await cat_wallet.generate_signed_transaction(
|
|
826
|
++
[uint64(10)], [cat_2_hash], DEFAULT_TX_CONFIG, memos=[[cat_2_hash]]
|
|
827
|
++
)
|
|
828
|
++
|
|
829
|
++
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
830
|
++
|
|
831
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
832
|
++
|
|
833
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 30)
|
|
834
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 30)
|
|
879
835
|
|
|
880
|
--
|
|
881
|
--
|
|
882
|
--
|
|
836
|
++
# Now we check that another wallet WAS created, even if autodiscovery was disabled
|
|
837
|
++
await time_out_assert(20, check_wallets, 2, wallet_node_2)
|
|
838
|
++
cat_wallet_2 = wallet_node_2.wallet_state_manager.wallets[uint32(2)]
|
|
839
|
++
assert isinstance(cat_wallet_2, CATWallet)
|
|
883
840
|
|
|
884
|
--
|
|
885
|
--
|
|
886
|
--
|
|
841
|
++
# Previous balance + balance that triggered creation in case of disabled autodiscovery
|
|
842
|
++
await time_out_assert(30, cat_wallet_2.get_confirmed_balance, 70)
|
|
843
|
++
await time_out_assert(30, cat_wallet_2.get_unconfirmed_balance, 70)
|
|
887
844
|
|
|
888
|
--
|
|
845
|
++
cat_hash = await cat_wallet.get_new_inner_hash()
|
|
846
|
++
tx_records = await cat_wallet_2.generate_signed_transaction([uint64(5)], [cat_hash], DEFAULT_TX_CONFIG)
|
|
847
|
++
await wallet.wallet_state_manager.add_pending_transactions(tx_records)
|
|
889
848
|
|
|
890
|
--
|
|
891
|
--
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 35)
|
|
849
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
892
850
|
|
|
893
|
--
|
|
894
|
--
|
|
895
|
--
|
|
851
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, 35)
|
|
852
|
++
await time_out_assert(20, cat_wallet.get_unconfirmed_balance, 35)
|
|
853
|
++
|
|
854
|
++
|
|
855
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
856
|
++
@pytest.mark.anyio
|
|
857
|
++
async def test_cat_change_detection(
|
|
858
|
++
self_hostname: str, one_wallet_and_one_simulator_services: SimulatorsAndWalletsServices, trusted: bool
|
|
859
|
++
) -> None:
|
|
860
|
++
num_blocks = 1
|
|
861
|
++
full_nodes, wallets, bt = one_wallet_and_one_simulator_services
|
|
862
|
++
full_node_api = full_nodes[0]._api
|
|
863
|
++
full_node_server = full_node_api.full_node.server
|
|
864
|
++
wallet_service_0 = wallets[0]
|
|
865
|
++
wallet_node_0 = wallet_service_0._node
|
|
866
|
++
wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
|
|
867
|
++
|
|
868
|
++
assert wallet_service_0.rpc_server is not None
|
|
869
|
++
|
|
870
|
++
client_0 = await WalletRpcClient.create(
|
|
871
|
++
bt.config["self_hostname"],
|
|
872
|
++
wallet_service_0.rpc_server.listen_port,
|
|
873
|
++
wallet_service_0.root_path,
|
|
874
|
++
wallet_service_0.config,
|
|
896
875
|
)
|
|
897
|
--
|
|
898
|
--
|
|
899
|
--
|
|
900
|
--
|
|
901
|
--
|
|
902
|
--
|
|
903
|
--
|
|
904
|
--
|
|
905
|
--
|
|
906
|
--
|
|
907
|
--
|
|
908
|
--
|
|
909
|
--
|
|
910
|
--
|
|
911
|
--
|
|
912
|
--
|
|
913
|
--
|
|
914
|
--
|
|
915
|
--
|
|
916
|
--
)
|
|
917
|
--
|
|
918
|
--
|
|
919
|
--
|
|
920
|
--
|
|
921
|
--
|
|
922
|
--
|
|
923
|
--
|
|
924
|
--
|
|
925
|
--
|
|
926
|
--
|
|
927
|
--
|
|
928
|
--
|
|
929
|
--
|
|
930
|
--
|
|
931
|
--
|
|
932
|
--
|
|
933
|
--
|
|
934
|
--
|
|
935
|
--
|
|
936
|
--
|
|
937
|
--
|
|
938
|
--
|
|
939
|
--
|
|
940
|
--
|
|
941
|
--
|
|
942
|
--
|
|
943
|
--
|
|
944
|
--
|
|
945
|
--
|
|
946
|
--
|
|
947
|
--
|
|
948
|
--
|
|
949
|
--
|
|
950
|
--
|
|
951
|
--
|
|
952
|
--
|
|
953
|
--
|
|
954
|
--
|
|
955
|
--
|
|
956
|
--
|
|
957
|
--
|
|
958
|
--
|
|
959
|
--
|
|
960
|
--
|
|
961
|
--
|
|
962
|
--
|
|
963
|
--
|
|
964
|
--
|
|
965
|
--
|
|
966
|
--
|
|
967
|
--
|
|
968
|
--
|
|
969
|
--
|
|
970
|
--
|
|
971
|
--
|
|
972
|
--
|
|
973
|
--
|
|
974
|
--
|
|
975
|
--
our_puzzle,
|
|
976
|
--
).get_tree_hash(),
|
|
977
|
--
cat_amount_0,
|
|
978
|
--
)
|
|
979
|
--
eve_spend = await wallet_node_0.wallet_state_manager.sign_transaction(
|
|
980
|
--
[
|
|
981
|
--
make_spend(
|
|
982
|
--
cat_coin,
|
|
983
|
--
cat_puzzle,
|
|
984
|
--
Program.to(
|
|
876
|
++
wallet_node_0.config["automatically_add_unknown_cats"] = True
|
|
877
|
++
|
|
878
|
++
if trusted:
|
|
879
|
++
wallet_node_0.config["trusted_peers"] = {
|
|
880
|
++
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
|
|
881
|
++
}
|
|
882
|
++
else:
|
|
883
|
++
wallet_node_0.config["trusted_peers"] = {}
|
|
884
|
++
|
|
885
|
++
await wallet_node_0.server.start_client(PeerInfo(self_hostname, uint16(full_node_server.get_port())), None)
|
|
886
|
++
await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
|
|
887
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
|
|
888
|
++
|
|
889
|
++
# Mint CAT to ourselves, immediately spend it to an unhinted puzzle hash that we have manually added to the DB
|
|
890
|
++
# We should pick up this coin as balance even though it is unhinted because it is "change"
|
|
891
|
++
intermediate_sk_un = master_sk_to_wallet_sk_unhardened_intermediate(wallet_node_0.wallet_state_manager.private_key)
|
|
892
|
++
pubkey_unhardened = _derive_path_unhardened(intermediate_sk_un, [100000000]).get_g1()
|
|
893
|
++
inner_puzhash = puzzle_hash_for_pk(pubkey_unhardened)
|
|
894
|
++
puzzlehash_unhardened = construct_cat_puzzle(
|
|
895
|
++
CAT_MOD, Program.to(None).get_tree_hash(), inner_puzhash
|
|
896
|
++
).get_tree_hash_precalc(inner_puzhash)
|
|
897
|
++
change_derivation = DerivationRecord(
|
|
898
|
++
uint32(0), puzzlehash_unhardened, pubkey_unhardened, WalletType.CAT, uint32(2), False
|
|
899
|
++
)
|
|
900
|
++
# Insert the derivation record before the wallet exists so that it is not subscribed to
|
|
901
|
++
await wallet_node_0.wallet_state_manager.puzzle_store.add_derivation_paths([change_derivation])
|
|
902
|
++
our_puzzle = await wallet_0.get_new_puzzle()
|
|
903
|
++
cat_puzzle = construct_cat_puzzle(
|
|
904
|
++
CAT_MOD,
|
|
905
|
++
Program.to(None).get_tree_hash(),
|
|
906
|
++
Program.to(1),
|
|
907
|
++
)
|
|
908
|
++
addr = encode_puzzle_hash(cat_puzzle.get_tree_hash(), "txch")
|
|
909
|
++
cat_amount_0 = uint64(100)
|
|
910
|
++
cat_amount_1 = uint64(5)
|
|
911
|
++
|
|
912
|
++
tx = await client_0.send_transaction(1, cat_amount_0, addr, DEFAULT_TX_CONFIG)
|
|
913
|
++
spend_bundle = tx.spend_bundle
|
|
914
|
++
assert spend_bundle is not None
|
|
915
|
++
|
|
916
|
++
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
917
|
++
await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
|
|
918
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
|
|
919
|
++
|
|
920
|
++
# Do the eve spend back to our wallet and add the CR layer
|
|
921
|
++
cat_coin = next(c for c in spend_bundle.additions() if c.amount == cat_amount_0)
|
|
922
|
++
next_coin = Coin(
|
|
923
|
++
cat_coin.name(),
|
|
924
|
++
construct_cat_puzzle(CAT_MOD, Program.to(None).get_tree_hash(), our_puzzle).get_tree_hash(),
|
|
925
|
++
cat_amount_0,
|
|
926
|
++
)
|
|
927
|
++
eve_spend = await wallet_node_0.wallet_state_manager.sign_transaction(
|
|
928
|
++
[
|
|
929
|
++
make_spend(
|
|
930
|
++
cat_coin,
|
|
931
|
++
cat_puzzle,
|
|
932
|
++
Program.to(
|
|
933
|
++
[
|
|
934
|
++
Program.to(
|
|
935
|
++
[
|
|
936
|
++
[51, our_puzzle.get_tree_hash(), cat_amount_0, [our_puzzle.get_tree_hash()]],
|
|
937
|
++
[51, None, -113, None, None],
|
|
938
|
++
]
|
|
939
|
++
),
|
|
940
|
++
None,
|
|
941
|
++
cat_coin.name(),
|
|
942
|
++
coin_as_list(cat_coin),
|
|
943
|
++
[cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_coin.amount],
|
|
944
|
++
0,
|
|
945
|
++
0,
|
|
946
|
++
]
|
|
947
|
++
),
|
|
948
|
++
),
|
|
949
|
++
make_spend(
|
|
950
|
++
next_coin,
|
|
951
|
++
construct_cat_puzzle(CAT_MOD, Program.to(None).get_tree_hash(), our_puzzle),
|
|
952
|
++
Program.to(
|
|
953
|
++
[
|
|
985
954
|
[
|
|
986
|
--
|
|
955
|
++
None,
|
|
956
|
++
(
|
|
957
|
++
1,
|
|
987
958
|
[
|
|
988
|
--
[
|
|
989
|
--
|
|
990
|
--
|
|
991
|
--
cat_amount_0,
|
|
992
|
--
[our_puzzle.get_tree_hash()],
|
|
993
|
--
],
|
|
994
|
--
[51, None, -113, None, None],
|
|
995
|
--
]
|
|
959
|
++
[51, inner_puzhash, cat_amount_1],
|
|
960
|
++
[51, bytes32([0] * 32), cat_amount_0 - cat_amount_1],
|
|
961
|
++
],
|
|
996
962
|
),
|
|
997
963
|
None,
|
|
998
|
--
|
|
999
|
--
|
|
1000
|
--
|
|
1001
|
--
|
|
1002
|
--
|
|
1003
|
--
|
|
1004
|
--
|
|
964
|
++
],
|
|
965
|
++
LineageProof(
|
|
966
|
++
cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_amount_0
|
|
967
|
++
).to_program(),
|
|
968
|
++
next_coin.name(),
|
|
969
|
++
coin_as_list(next_coin),
|
|
970
|
++
[next_coin.parent_coin_info, our_puzzle.get_tree_hash(), next_coin.amount],
|
|
971
|
++
0,
|
|
972
|
++
0,
|
|
973
|
++
]
|
|
1005
974
|
),
|
|
1006
|
--
|
|
1007
|
--
|
|
1008
|
--
|
|
1009
|
--
|
|
1010
|
--
|
|
1011
|
--
|
|
1012
|
--
|
|
1013
|
--
Program.to(
|
|
1014
|
--
[
|
|
1015
|
--
[
|
|
1016
|
--
None,
|
|
1017
|
--
(
|
|
1018
|
--
1,
|
|
1019
|
--
[
|
|
1020
|
--
[51, inner_puzhash, cat_amount_1],
|
|
1021
|
--
[51, bytes32([0] * 32), cat_amount_0 - cat_amount_1],
|
|
1022
|
--
],
|
|
1023
|
--
),
|
|
1024
|
--
None,
|
|
1025
|
--
],
|
|
1026
|
--
LineageProof(
|
|
1027
|
--
cat_coin.parent_coin_info, Program.to(1).get_tree_hash(), cat_amount_0
|
|
1028
|
--
).to_program(),
|
|
1029
|
--
next_coin.name(),
|
|
1030
|
--
coin_as_list(next_coin),
|
|
1031
|
--
[next_coin.parent_coin_info, our_puzzle.get_tree_hash(), next_coin.amount],
|
|
1032
|
--
0,
|
|
1033
|
--
0,
|
|
1034
|
--
]
|
|
1035
|
--
),
|
|
1036
|
--
),
|
|
1037
|
--
],
|
|
1038
|
--
)
|
|
1039
|
--
await client_0.push_tx(eve_spend)
|
|
1040
|
--
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, eve_spend.name())
|
|
1041
|
--
await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
|
|
1042
|
--
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
|
|
975
|
++
),
|
|
976
|
++
],
|
|
977
|
++
)
|
|
978
|
++
await client_0.push_tx(eve_spend)
|
|
979
|
++
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, eve_spend.name())
|
|
980
|
++
await full_node_api.farm_blocks_to_wallet(count=num_blocks, wallet=wallet_0)
|
|
981
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
|
|
1043
982
|
|
|
1044
|
--
|
|
1045
|
--
|
|
1046
|
--
|
|
1047
|
--
|
|
983
|
++
await time_out_assert(20, check_wallets, 2, wallet_node_0)
|
|
984
|
++
cat_wallet = wallet_node_0.wallet_state_manager.wallets[uint32(2)]
|
|
985
|
++
await time_out_assert(20, cat_wallet.get_confirmed_balance, cat_amount_1)
|
|
986
|
++
assert not full_node_api.full_node.subscriptions.has_puzzle_subscription(puzzlehash_unhardened)
|
|
1048
987
|
|
|
1049
988
|
|
|
1050
989
|
@pytest.mark.anyio
|
|
1051
990
|
async def test_unacknowledged_cat_table() -> None:
|
|
1052
991
|
db_name = Path(tempfile.TemporaryDirectory().name).joinpath("test.sqlite")
|
|
1053
992
|
db_name.parent.mkdir(parents=True, exist_ok=True)
|
|
1054
|
--
async with DBWrapper2.managed(
|
|
1055
|
--
database=db_name,
|
|
1056
|
--
) as db_wrapper:
|
|
993
|
++
async with DBWrapper2.managed(database=db_name) as db_wrapper:
|
|
1057
994
|
interested_store = await WalletInterestedStore.create(db_wrapper)
|
|
1058
995
|
|
|
1059
996
|
def asset_id(i: int) -> bytes32:
|
|
@@@ -1062,22 -1062,22 +999,10 @@@
|
|
|
1062
999
|
def coin_state(i: int) -> CoinState:
|
|
1063
1000
|
return CoinState(Coin(bytes32([0] * 32), bytes32([0] * 32), uint64(i)), None, None)
|
|
1064
1001
|
|
|
1065
|
--
await interested_store.add_unacknowledged_coin_state(
|
|
1066
|
--
|
|
1067
|
--
coin_state(0),
|
|
1068
|
--
None,
|
|
1069
|
--
)
|
|
1070
|
--
await interested_store.add_unacknowledged_coin_state(
|
|
1071
|
--
asset_id(1),
|
|
1072
|
--
coin_state(1),
|
|
1073
|
--
100,
|
|
1074
|
--
)
|
|
1002
|
++
await interested_store.add_unacknowledged_coin_state(asset_id(0), coin_state(0), None)
|
|
1003
|
++
await interested_store.add_unacknowledged_coin_state(asset_id(1), coin_state(1), 100)
|
|
1075
1004
|
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(0)) == [(coin_state(0), 0)]
|
|
1076
|
--
await interested_store.add_unacknowledged_coin_state(
|
|
1077
|
--
asset_id(0),
|
|
1078
|
--
coin_state(0),
|
|
1079
|
--
None,
|
|
1080
|
--
)
|
|
1005
|
++
await interested_store.add_unacknowledged_coin_state(asset_id(0), coin_state(0), None)
|
|
1081
1006
|
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(0)) == [(coin_state(0), 0)]
|
|
1082
1007
|
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(1)) == [(coin_state(1), 100)]
|
|
1083
1008
|
assert await interested_store.get_unacknowledged_states_for_asset_id(asset_id(2)) == []
|
|
@@@ -1,17 -1,17 +1,17 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import replace
|
|
4
|
--
from typing import Any, Dict, List, Optional
|
|
4
|
++
from typing import Any, Dict, List, Optional, cast
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
from chia_rs import G2Element
|
|
8
8
|
|
|
9
|
--
from chia.clvm.spend_sim import sim_and_client
|
|
9
|
++
from chia.clvm.spend_sim import CostLogger, SimClient, SpendSim, sim_and_client
|
|
10
10
|
from chia.types.blockchain_format.coin import Coin
|
|
11
11
|
from chia.types.blockchain_format.program import Program
|
|
12
12
|
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
13
13
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
14
|
--
from chia.types.coin_spend import
|
|
14
|
++
from chia.types.coin_spend import make_spend
|
|
15
15
|
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
|
16
16
|
from chia.types.spend_bundle import SpendBundle
|
|
17
17
|
from chia.util.ints import uint64
|
|
@@@ -21,11 -21,11 +21,11 @@@ from chia.wallet.cat_wallet.cat_utils i
|
|
|
21
21
|
construct_cat_puzzle,
|
|
22
22
|
unsigned_spend_bundle_for_spendable_cats,
|
|
23
23
|
)
|
|
24
|
--
from chia.wallet.conditions import
|
|
24
|
++
from chia.wallet.conditions import AssertPuzzleAnnouncement, ConditionValidTimes
|
|
25
25
|
from chia.wallet.outer_puzzles import AssetType
|
|
26
26
|
from chia.wallet.payment import Payment
|
|
27
27
|
from chia.wallet.puzzle_drivers import PuzzleInfo
|
|
28
|
--
from chia.wallet.trading.offer import OFFER_MOD,
|
|
28
|
++
from chia.wallet.trading.offer import OFFER_MOD, Offer
|
|
29
29
|
|
|
30
30
|
acs = Program.to(1)
|
|
31
31
|
acs_ph = acs.get_tree_hash()
|
|
@@@ -33,7 -33,7 +33,8 @@@
|
|
|
33
33
|
|
|
34
34
|
# Some methods mapping strings to CATs
|
|
35
35
|
def str_to_tail(tail_str: str) -> Program:
|
|
36
|
--
|
|
36
|
++
# TODO: Remove cast when we improve typing
|
|
37
|
++
return cast(Program, Program.to([3, [], [1, tail_str], []]))
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
def str_to_tail_hash(tail_str: str) -> bytes32:
|
|
@@@ -46,24 -46,24 +47,22 @@@ def str_to_cat_hash(tail_str: str) -> b
|
|
|
46
47
|
|
|
47
48
|
# This method takes a dictionary of strings mapping to amounts and generates the appropriate CAT/XCH coins
|
|
48
49
|
async def generate_coins(
|
|
49
|
--
sim,
|
|
50
|
--
sim_client,
|
|
51
|
--
requested_coins: Dict[Optional[str], List[uint64]],
|
|
50
|
++
sim: SpendSim, sim_client: SimClient, requested_coins: Dict[Optional[str], List[int]]
|
|
52
51
|
) -> Dict[Optional[str], List[Coin]]:
|
|
53
52
|
await sim.farm_block(acs_ph)
|
|
54
|
--
parent_coin
|
|
53
|
++
parent_coin = [cr.coin for cr in await sim_client.get_coin_records_by_puzzle_hash(acs_ph)][0]
|
|
55
54
|
|
|
56
55
|
# We need to gather a list of initial coins to create as well as spends that do the eve spend for every CAT
|
|
57
|
--
payments
|
|
58
|
--
cat_bundles
|
|
56
|
++
payments = []
|
|
57
|
++
cat_bundles = []
|
|
59
58
|
for tail_str, amounts in requested_coins.items():
|
|
60
59
|
for amount in amounts:
|
|
61
60
|
if tail_str:
|
|
62
|
--
tail
|
|
61
|
++
tail = str_to_tail(tail_str) # Making a fake but unique TAIL
|
|
63
62
|
tail_hash = tail.get_tree_hash()
|
|
64
|
--
cat_puzzle
|
|
63
|
++
cat_puzzle = construct_cat_puzzle(CAT_MOD, tail_hash, acs)
|
|
65
64
|
cat_puzzle_hash = cat_puzzle.get_tree_hash()
|
|
66
|
--
payments.append(Payment(cat_puzzle_hash, amount))
|
|
65
|
++
payments.append(Payment(cat_puzzle_hash, uint64(amount)))
|
|
67
66
|
cat_bundles.append(
|
|
68
67
|
unsigned_spend_bundle_for_spendable_cats(
|
|
69
68
|
CAT_MOD,
|
|
@@@ -78,18 -78,18 +77,11 @@@
|
|
|
78
77
|
)
|
|
79
78
|
)
|
|
80
79
|
else:
|
|
81
|
--
payments.append(Payment(acs_ph, amount))
|
|
80
|
++
payments.append(Payment(acs_ph, uint64(amount)))
|
|
82
81
|
|
|
83
82
|
# This bundle creates all of the initial coins
|
|
84
83
|
parent_bundle = SpendBundle(
|
|
85
|
--
[
|
|
86
|
--
make_spend(
|
|
87
|
--
parent_coin,
|
|
88
|
--
acs,
|
|
89
|
--
Program.to([[51, p.puzzle_hash, p.amount] for p in payments]),
|
|
90
|
--
)
|
|
91
|
--
],
|
|
92
|
--
G2Element(),
|
|
84
|
++
[make_spend(parent_coin, acs, Program.to([[51, p.puzzle_hash, p.amount] for p in payments]))], G2Element()
|
|
93
85
|
)
|
|
94
86
|
|
|
95
87
|
# Then we aggregate it with all of the eve spends
|
|
@@@ -101,7 -101,7 +93,7 @@@
|
|
|
101
93
|
for tail_str, _ in requested_coins.items():
|
|
102
94
|
if tail_str:
|
|
103
95
|
tail_hash = str_to_tail_hash(tail_str)
|
|
104
|
--
cat_ph
|
|
96
|
++
cat_ph = construct_cat_puzzle(CAT_MOD, tail_hash, acs).get_tree_hash()
|
|
105
97
|
coin_dict[tail_str] = [
|
|
106
98
|
cr.coin for cr in await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False)
|
|
107
99
|
]
|
|
@@@ -123,14 -123,14 +115,14 @@@
|
|
|
123
115
|
# but doesn't bother with non-offer announcements
|
|
124
116
|
def generate_secure_bundle(
|
|
125
117
|
selected_coins: List[Coin],
|
|
126
|
--
announcements: List[
|
|
118
|
++
announcements: List[AssertPuzzleAnnouncement],
|
|
127
119
|
offered_amount: uint64,
|
|
128
120
|
tail_str: Optional[str] = None,
|
|
129
121
|
) -> SpendBundle:
|
|
130
|
--
announcement_assertions
|
|
131
|
--
selected_coin_amount
|
|
132
|
--
non_primaries
|
|
133
|
--
inner_solution: List[List] = [
|
|
122
|
++
announcement_assertions = [a.to_program() for a in announcements]
|
|
123
|
++
selected_coin_amount = sum([c.amount for c in selected_coins])
|
|
124
|
++
non_primaries = [] if len(selected_coins) < 2 else selected_coins[1:]
|
|
125
|
++
inner_solution: List[List[Any]] = [
|
|
134
126
|
[51, Offer.ph(), offered_amount], # Offered coin
|
|
135
127
|
[51, acs_ph, uint64(selected_coin_amount - offered_amount)], # Change
|
|
136
128
|
*announcement_assertions,
|
|
@@@ -149,7 -149,7 +141,7 @@@
|
|
|
149
141
|
G2Element(),
|
|
150
142
|
)
|
|
151
143
|
else:
|
|
152
|
--
spendable_cats
|
|
144
|
++
spendable_cats = [
|
|
153
145
|
SpendableCAT(
|
|
154
146
|
c,
|
|
155
147
|
str_to_tail_hash(tail_str),
|
|
@@@ -168,180 -168,180 +160,135 @@@
|
|
|
168
160
|
return bundle
|
|
169
161
|
|
|
170
162
|
|
|
171
|
--
|
|
172
|
--
|
|
173
|
--
async
|
|
174
|
--
|
|
175
|
--
|
|
176
|
--
|
|
177
|
--
|
|
178
|
--
|
|
179
|
--
|
|
180
|
--
|
|
181
|
--
|
|
182
|
--
|
|
183
|
--
|
|
184
|
--
|
|
185
|
--
|
|
186
|
--
|
|
187
|
--
|
|
188
|
--
|
|
189
|
--
|
|
190
|
--
|
|
191
|
--
|
|
192
|
--
|
|
193
|
--
|
|
194
|
--
|
|
195
|
--
|
|
196
|
--
|
|
197
|
--
|
|
198
|
--
|
|
199
|
--
|
|
200
|
--
|
|
201
|
--
|
|
202
|
--
|
|
203
|
--
|
|
204
|
--
|
|
205
|
--
|
|
206
|
--
|
|
207
|
--
|
|
208
|
--
|
|
209
|
--
|
|
210
|
--
|
|
211
|
--
|
|
212
|
--
|
|
213
|
--
|
|
214
|
--
|
|
215
|
--
|
|
216
|
--
|
|
217
|
--
|
|
218
|
--
|
|
219
|
--
|
|
220
|
--
|
|
221
|
--
|
|
222
|
--
|
|
223
|
--
|
|
224
|
--
|
|
225
|
--
|
|
226
|
--
|
|
227
|
--
|
|
228
|
--
|
|
229
|
--
|
|
230
|
--
|
|
231
|
--
|
|
232
|
--
|
|
233
|
--
|
|
234
|
--
)
|
|
235
|
--
|
|
236
|
--
|
|
237
|
--
|
|
238
|
--
|
|
239
|
--
|
|
240
|
--
|
|
241
|
--
|
|
242
|
--
|
|
243
|
--
|
|
244
|
--
|
|
245
|
--
|
|
246
|
--
)
|
|
247
|
--
|
|
248
|
--
|
|
249
|
--
|
|
250
|
--
|
|
251
|
--
|
|
252
|
--
)
|
|
253
|
--
|
|
254
|
--
|
|
255
|
--
|
|
256
|
--
|
|
257
|
--
|
|
258
|
--
|
|
259
|
--
|
|
260
|
--
|
|
261
|
--
|
|
262
|
--
|
|
263
|
--
|
|
264
|
--
|
|
265
|
--
|
|
266
|
--
|
|
267
|
--
|
|
268
|
--
|
|
269
|
--
|
|
270
|
--
|
|
271
|
--
|
|
272
|
--
|
|
273
|
--
|
|
274
|
--
|
|
275
|
--
|
|
276
|
--
|
|
277
|
--
|
|
278
|
--
|
|
279
|
--
|
|
280
|
--
|
|
281
|
--
|
|
282
|
--
|
|
283
|
--
|
|
284
|
--
|
|
285
|
--
|
|
286
|
--
|
|
287
|
--
|
|
288
|
--
|
|
289
|
--
str_to_tail_hash("blue"): 2000,
|
|
290
|
--
}
|
|
291
|
--
assert new_offer.get_requested_amounts() == {None: 900, str_to_tail_hash("red"): 350}
|
|
292
|
--
assert new_offer.summary() == (
|
|
293
|
--
{
|
|
294
|
--
"xch": 1000,
|
|
295
|
--
str_to_tail_hash("red").hex(): 350,
|
|
296
|
--
str_to_tail_hash("blue").hex(): 2000,
|
|
297
|
--
},
|
|
298
|
--
{"xch": 900, str_to_tail_hash("red").hex(): 350},
|
|
299
|
--
driver_dict_as_infos,
|
|
300
|
--
ConditionValidTimes(),
|
|
301
|
--
)
|
|
302
|
--
assert new_offer.get_pending_amounts() == {
|
|
303
|
--
"xch": 1200,
|
|
304
|
--
str_to_tail_hash("red").hex(): 350,
|
|
305
|
--
str_to_tail_hash("blue").hex(): 3000,
|
|
306
|
--
}
|
|
307
|
--
assert new_offer.is_valid()
|
|
308
|
--
|
|
309
|
--
# Test preventing TAIL from running during exchange
|
|
310
|
--
blue_cat_puz: Program = construct_cat_puzzle(CAT_MOD, str_to_tail_hash("blue"), OFFER_MOD)
|
|
311
|
--
blue_spend: CoinSpend = make_spend(
|
|
312
|
--
Coin(bytes32(32), blue_cat_puz.get_tree_hash(), uint64(0)),
|
|
313
|
--
blue_cat_puz,
|
|
314
|
--
Program.to([[bytes32(32), [bytes32(32), 200, ["hey there"]]]]),
|
|
315
|
--
)
|
|
316
|
--
new_spends_list: List[CoinSpend] = [blue_spend, *new_offer.to_spend_bundle().coin_spends]
|
|
317
|
--
tail_offer: Offer = Offer.from_spend_bundle(SpendBundle(new_spends_list, G2Element()))
|
|
318
|
--
valid_spend = tail_offer.to_valid_spend(bytes32(32))
|
|
319
|
--
real_blue_spend = [spend for spend in valid_spend.coin_spends if b"hey there" in bytes(spend)][0]
|
|
320
|
--
real_blue_spend_replaced = replace(
|
|
321
|
--
real_blue_spend,
|
|
322
|
--
solution=SerializedProgram.from_program(
|
|
323
|
--
real_blue_spend.solution.to_program().replace(
|
|
324
|
--
ffrfrf=Program.to(-113), ffrfrr=Program.to([str_to_tail("blue"), []])
|
|
325
|
--
)
|
|
326
|
--
),
|
|
327
|
--
)
|
|
328
|
--
valid_spend = SpendBundle(
|
|
329
|
--
[real_blue_spend_replaced, *[spend for spend in valid_spend.coin_spends if spend != real_blue_spend]],
|
|
330
|
--
G2Element(),
|
|
331
|
--
)
|
|
332
|
--
with pytest.raises(ValueError, match="clvm raise"):
|
|
333
|
--
valid_spend.additions()
|
|
163
|
++
@pytest.mark.anyio()
|
|
164
|
++
async def test_complex_offer(cost_logger: CostLogger) -> None:
|
|
165
|
++
async with sim_and_client() as (sim, sim_client):
|
|
166
|
++
coins_needed = {None: [500, 400, 300], "red": [250, 100], "blue": [3000]}
|
|
167
|
++
all_coins = await generate_coins(sim, sim_client, coins_needed)
|
|
168
|
++
chia_coins = all_coins[None]
|
|
169
|
++
red_coins = all_coins["red"]
|
|
170
|
++
blue_coins = all_coins["blue"]
|
|
171
|
++
|
|
172
|
++
driver_dict = {
|
|
173
|
++
str_to_tail_hash("red"): PuzzleInfo(
|
|
174
|
++
{"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("red").hex()}
|
|
175
|
++
),
|
|
176
|
++
str_to_tail_hash("blue"): PuzzleInfo(
|
|
177
|
++
{"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("blue").hex()}
|
|
178
|
++
),
|
|
179
|
++
}
|
|
180
|
++
driver_dict_as_infos = {key.hex(): value.info for key, value in driver_dict.items()}
|
|
181
|
++
|
|
182
|
++
# Create an XCH Offer for RED
|
|
183
|
++
chia_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
|
|
184
|
++
str_to_tail_hash("red"): [Payment(acs_ph, uint64(100), [b"memo"]), Payment(acs_ph, uint64(200), [b"memo"])]
|
|
185
|
++
}
|
|
186
|
++
chia_notarized_payments = Offer.notarize_payments(chia_requested_payments, chia_coins)
|
|
187
|
++
chia_announcements = Offer.calculate_announcements(chia_notarized_payments, driver_dict)
|
|
188
|
++
chia_secured_bundle = generate_secure_bundle(chia_coins, chia_announcements, uint64(1000))
|
|
189
|
++
chia_offer = Offer(chia_notarized_payments, chia_secured_bundle, driver_dict)
|
|
190
|
++
assert not chia_offer.is_valid()
|
|
191
|
++
|
|
192
|
++
# Create a RED Offer for XCH
|
|
193
|
++
red_coins_1 = red_coins[0:1]
|
|
194
|
++
red_coins_2 = red_coins[1:]
|
|
195
|
++
red_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
|
|
196
|
++
None: [Payment(acs_ph, uint64(300), [b"red memo"]), Payment(acs_ph, uint64(350), [b"red memo"])]
|
|
197
|
++
}
|
|
198
|
++
red_notarized_payments = Offer.notarize_payments(red_requested_payments, red_coins_1)
|
|
199
|
++
red_announcements = Offer.calculate_announcements(red_notarized_payments, driver_dict)
|
|
200
|
++
red_secured_bundle = generate_secure_bundle(
|
|
201
|
++
red_coins_1, red_announcements, uint64(sum([c.amount for c in red_coins_1])), tail_str="red"
|
|
202
|
++
)
|
|
203
|
++
red_offer = Offer(red_notarized_payments, red_secured_bundle, driver_dict)
|
|
204
|
++
assert not red_offer.is_valid()
|
|
205
|
++
|
|
206
|
++
red_requested_payments_2: Dict[Optional[bytes32], List[Payment]] = {
|
|
207
|
++
None: [Payment(acs_ph, uint64(50), [b"red memo"])]
|
|
208
|
++
}
|
|
209
|
++
red_notarized_payments_2 = Offer.notarize_payments(red_requested_payments_2, red_coins_2)
|
|
210
|
++
red_announcements_2 = Offer.calculate_announcements(red_notarized_payments_2, driver_dict)
|
|
211
|
++
red_secured_bundle_2 = generate_secure_bundle(
|
|
212
|
++
red_coins_2, red_announcements_2, uint64(sum([c.amount for c in red_coins_2])), tail_str="red"
|
|
213
|
++
)
|
|
214
|
++
red_offer_2 = Offer(red_notarized_payments_2, red_secured_bundle_2, driver_dict)
|
|
215
|
++
assert not red_offer_2.is_valid()
|
|
216
|
++
|
|
217
|
++
# Test aggregation of offers
|
|
218
|
++
new_offer = Offer.aggregate([chia_offer, red_offer, red_offer_2])
|
|
219
|
++
assert new_offer.get_offered_amounts() == {None: 1000, str_to_tail_hash("red"): 350}
|
|
220
|
++
assert new_offer.get_requested_amounts() == {None: 700, str_to_tail_hash("red"): 300}
|
|
221
|
++
assert new_offer.is_valid()
|
|
222
|
++
|
|
223
|
++
# Create yet another offer of BLUE for XCH and RED
|
|
224
|
++
blue_requested_payments: Dict[Optional[bytes32], List[Payment]] = {
|
|
225
|
++
None: [Payment(acs_ph, uint64(200), [b"blue memo"])],
|
|
226
|
++
str_to_tail_hash("red"): [Payment(acs_ph, uint64(50), [b"blue memo"])],
|
|
227
|
++
}
|
|
228
|
++
blue_notarized_payments = Offer.notarize_payments(blue_requested_payments, blue_coins)
|
|
229
|
++
blue_announcements = Offer.calculate_announcements(blue_notarized_payments, driver_dict)
|
|
230
|
++
blue_secured_bundle = generate_secure_bundle(blue_coins, blue_announcements, uint64(2000), tail_str="blue")
|
|
231
|
++
blue_offer = Offer(blue_notarized_payments, blue_secured_bundle, driver_dict)
|
|
232
|
++
assert not blue_offer.is_valid()
|
|
233
|
++
|
|
234
|
++
# Test a re-aggregation
|
|
235
|
++
new_offer = Offer.aggregate([new_offer, blue_offer])
|
|
236
|
++
assert new_offer.get_offered_amounts() == {
|
|
237
|
++
None: 1000,
|
|
238
|
++
str_to_tail_hash("red"): 350,
|
|
239
|
++
str_to_tail_hash("blue"): 2000,
|
|
240
|
++
}
|
|
241
|
++
assert new_offer.get_requested_amounts() == {None: 900, str_to_tail_hash("red"): 350}
|
|
242
|
++
assert new_offer.summary() == (
|
|
243
|
++
{"xch": 1000, str_to_tail_hash("red").hex(): 350, str_to_tail_hash("blue").hex(): 2000},
|
|
244
|
++
{"xch": 900, str_to_tail_hash("red").hex(): 350},
|
|
245
|
++
driver_dict_as_infos,
|
|
246
|
++
ConditionValidTimes(),
|
|
247
|
++
)
|
|
248
|
++
assert new_offer.get_pending_amounts() == {
|
|
249
|
++
"xch": 1200,
|
|
250
|
++
str_to_tail_hash("red").hex(): 350,
|
|
251
|
++
str_to_tail_hash("blue").hex(): 3000,
|
|
252
|
++
}
|
|
253
|
++
assert new_offer.is_valid()
|
|
254
|
++
|
|
255
|
++
# Test preventing TAIL from running during exchange
|
|
256
|
++
blue_cat_puz = construct_cat_puzzle(CAT_MOD, str_to_tail_hash("blue"), OFFER_MOD)
|
|
257
|
++
random_hash = bytes32([0] * 32)
|
|
258
|
++
blue_spend = make_spend(
|
|
259
|
++
Coin(random_hash, blue_cat_puz.get_tree_hash(), uint64(0)),
|
|
260
|
++
blue_cat_puz,
|
|
261
|
++
Program.to([[random_hash, [random_hash, 200, ["hey there"]]]]),
|
|
262
|
++
)
|
|
263
|
++
new_spends_list = [blue_spend, *new_offer.to_spend_bundle().coin_spends]
|
|
264
|
++
tail_offer = Offer.from_spend_bundle(SpendBundle(new_spends_list, G2Element()))
|
|
265
|
++
valid_spend = tail_offer.to_valid_spend(random_hash)
|
|
266
|
++
real_blue_spend = [spend for spend in valid_spend.coin_spends if b"hey there" in bytes(spend)][0]
|
|
267
|
++
real_blue_spend_replaced = replace(
|
|
268
|
++
real_blue_spend,
|
|
269
|
++
solution=SerializedProgram.from_program(
|
|
270
|
++
real_blue_spend.solution.to_program().replace(
|
|
271
|
++
ffrfrf=Program.to(-113), ffrfrr=Program.to([str_to_tail("blue"), []])
|
|
272
|
++
)
|
|
273
|
++
),
|
|
274
|
++
)
|
|
275
|
++
valid_spend = SpendBundle(
|
|
276
|
++
[real_blue_spend_replaced, *[spend for spend in valid_spend.coin_spends if spend != real_blue_spend]],
|
|
277
|
++
G2Element(),
|
|
278
|
++
)
|
|
279
|
++
with pytest.raises(ValueError, match="clvm raise"):
|
|
280
|
++
valid_spend.additions()
|
|
334
281
|
|
|
335
|
--
|
|
336
|
--
|
|
282
|
++
# Test (de)serialization
|
|
283
|
++
assert Offer.from_bytes(bytes(new_offer)) == new_offer
|
|
337
284
|
|
|
338
|
--
|
|
339
|
--
|
|
285
|
++
# Test compression
|
|
286
|
++
assert Offer.from_compressed(new_offer.compress()) == new_offer
|
|
340
287
|
|
|
341
|
--
|
|
342
|
--
|
|
343
|
--
|
|
288
|
++
# Make sure we can actually spend the offer once it's valid
|
|
289
|
++
arbitrage_ph = Program.to([3, [], [], 1]).get_tree_hash()
|
|
290
|
++
offer_bundle = new_offer.to_valid_spend(arbitrage_ph)
|
|
344
291
|
|
|
345
|
--
|
|
346
|
--
|
|
347
|
--
|
|
292
|
++
result = await sim_client.push_tx(cost_logger.add_cost("Complex Offer", offer_bundle))
|
|
293
|
++
assert result == (MempoolInclusionStatus.SUCCESS, None)
|
|
294
|
++
await sim.farm_block()
|
|
@@@ -1814,7 -1814,7 +1814,7 @@@ class TestCATTrades
|
|
|
1814
1814
|
return wallet_node_taker._tx_messages_in_progress == {}
|
|
1815
1815
|
|
|
1816
1816
|
for _ in range(10):
|
|
1817
|
--
|
|
1817
|
++
await wallet_node_taker._resend_queue()
|
|
1818
1818
|
await time_out_assert(5, check_wallet_cache_empty, True)
|
|
1819
1819
|
offer_tx_records: List[TransactionRecord] = await wallet_node_maker.wallet_state_manager.tx_store.get_not_sent()
|
|
1820
1820
|
await full_node.process_transaction_records(records=offer_tx_records)
|
|
@@@ -224,7 -224,7 +224,7 @@@ def test_proposal() -> None
|
|
|
224
224
|
|
|
225
225
|
with pytest.raises(ValueError) as e_info:
|
|
226
226
|
conditions_dict_for_solution(full_proposal, repeat_solution_2, INFINITE_COST)
|
|
227
|
--
assert e_info.value.args[0]
|
|
227
|
++
assert "clvm raise" in e_info.value.args[0]
|
|
228
228
|
|
|
229
229
|
# Test Launch
|
|
230
230
|
current_yes_votes = 0
|
|
@@@ -937,7 -937,7 +937,7 @@@ def test_lockup() -> None
|
|
|
937
937
|
)
|
|
938
938
|
with pytest.raises(ValueError) as e_info:
|
|
939
939
|
conds = full_lockup_puz.run(revote_solution)
|
|
940
|
--
assert e_info.value.args[0]
|
|
940
|
++
assert "clvm raise" in e_info.value.args[0]
|
|
941
941
|
|
|
942
942
|
# Test vote removal
|
|
943
943
|
solution = Program.to(
|
|
@@@ -23,6 -23,6 +23,8 @@@ from chia.wallet.singleton import creat
|
|
|
23
23
|
from chia.wallet.util.address_type import AddressType
|
|
24
24
|
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
|
|
25
25
|
from chia.wallet.util.wallet_types import WalletType
|
|
26
|
++
from chia.wallet.wallet_state_manager import WalletStateManager
|
|
27
|
++
from tests.environments.wallet import WalletStateTransition, WalletTestFramework
|
|
26
28
|
from tests.util.setup_nodes import OldSimulatorsAndWallets
|
|
27
29
|
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
|
|
28
30
|
|
|
@@@ -109,9 -109,9 +111,7 @@@ class TestDIDWallet
|
|
|
109
111
|
|
|
110
112
|
#######################
|
|
111
113
|
all_node_0_wallets = await wallet_node_0.wallet_state_manager.user_store.get_all_wallet_info_entries()
|
|
112
|
--
print(f"Node 0: {all_node_0_wallets}")
|
|
113
114
|
all_node_1_wallets = await wallet_node_1.wallet_state_manager.user_store.get_all_wallet_info_entries()
|
|
114
|
--
print(f"Node 1: {all_node_1_wallets}")
|
|
115
115
|
assert (
|
|
116
116
|
json.loads(all_node_0_wallets[1].data)["current_inner"]
|
|
117
117
|
== json.loads(all_node_1_wallets[1].data)["current_inner"]
|
|
@@@ -1268,9 -1268,9 +1268,7 @@@
|
|
|
1268
1268
|
|
|
1269
1269
|
#######################
|
|
1270
1270
|
all_node_0_wallets = await wallet_node_0.wallet_state_manager.user_store.get_all_wallet_info_entries()
|
|
1271
|
--
print(f"Node 0: {all_node_0_wallets}")
|
|
1272
1271
|
all_node_1_wallets = await wallet_node_1.wallet_state_manager.user_store.get_all_wallet_info_entries()
|
|
1273
|
--
print(f"Node 1: {all_node_1_wallets}")
|
|
1274
1272
|
assert len(all_node_0_wallets) == len(all_node_1_wallets)
|
|
1275
1273
|
|
|
1276
1274
|
# Note that the inner program we expect is different than the on-chain inner.
|
|
@@@ -1369,3 -1369,3 +1367,68 @@@
|
|
|
1369
1367
|
await time_out_assert(30, get_wallet_num, 2, wallet_node_2.wallet_state_manager)
|
|
1370
1368
|
did_wallet_2 = wallet_node_2.wallet_state_manager.get_wallet(uint32(2), DIDWallet)
|
|
1371
1369
|
assert did_info == did_wallet_2.did_info
|
|
1370
|
++
|
|
1371
|
++
|
|
1372
|
++
@pytest.mark.parametrize(
|
|
1373
|
++
"wallet_environments",
|
|
1374
|
++
[
|
|
1375
|
++
{
|
|
1376
|
++
"num_environments": 1,
|
|
1377
|
++
"blocks_needed": [1],
|
|
1378
|
++
}
|
|
1379
|
++
],
|
|
1380
|
++
indirect=True,
|
|
1381
|
++
)
|
|
1382
|
++
@pytest.mark.anyio
|
|
1383
|
++
async def test_did_coin_records(wallet_environments: WalletTestFramework, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1384
|
++
# Setup
|
|
1385
|
++
wallet_node = wallet_environments.environments[0].node
|
|
1386
|
++
wallet = wallet_environments.environments[0].xch_wallet
|
|
1387
|
++
client = wallet_environments.environments[0].rpc_client
|
|
1388
|
++
|
|
1389
|
++
# Generate DID wallet
|
|
1390
|
++
did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(wallet_node.wallet_state_manager, wallet, uint64(1))
|
|
1391
|
++
|
|
1392
|
++
await wallet_environments.process_pending_states(
|
|
1393
|
++
[
|
|
1394
|
++
WalletStateTransition(
|
|
1395
|
++
pre_block_balance_updates={
|
|
1396
|
++
1: {"set_remainder": True},
|
|
1397
|
++
2: {"init": True, "set_remainder": True},
|
|
1398
|
++
},
|
|
1399
|
++
post_block_balance_updates={
|
|
1400
|
++
1: {"set_remainder": True},
|
|
1401
|
++
2: {"set_remainder": True},
|
|
1402
|
++
},
|
|
1403
|
++
),
|
|
1404
|
++
WalletStateTransition(),
|
|
1405
|
++
]
|
|
1406
|
++
)
|
|
1407
|
++
|
|
1408
|
++
# When transfer_did doesn't push the transactions automatically, this monkeypatching is no longer necessary
|
|
1409
|
++
with monkeypatch.context() as m:
|
|
1410
|
++
|
|
1411
|
++
async def nothing(*args) -> None:
|
|
1412
|
++
pass
|
|
1413
|
++
|
|
1414
|
++
m.setattr(WalletStateManager, "add_pending_transactions", nothing)
|
|
1415
|
++
for _ in range(0, 2):
|
|
1416
|
++
[tx] = await did_wallet.transfer_did(
|
|
1417
|
++
await wallet.get_puzzle_hash(new=False), uint64(0), True, wallet_environments.tx_config
|
|
1418
|
++
)
|
|
1419
|
++
assert tx.spend_bundle is not None
|
|
1420
|
++
await client.push_tx(tx.spend_bundle)
|
|
1421
|
++
await wallet_environments.process_pending_states(
|
|
1422
|
++
[
|
|
1423
|
++
WalletStateTransition(
|
|
1424
|
++
pre_block_balance_updates={},
|
|
1425
|
++
post_block_balance_updates={
|
|
1426
|
++
1: {"set_remainder": True},
|
|
1427
|
++
2: {"set_remainder": True},
|
|
1428
|
++
},
|
|
1429
|
++
),
|
|
1430
|
++
WalletStateTransition(),
|
|
1431
|
++
]
|
|
1432
|
++
)
|
|
1433
|
++
|
|
1434
|
++
assert len(await wallet.wallet_state_manager.get_spendable_coins_for_wallet(did_wallet.id())) == 1
|
|
@@@ -233,6 -233,6 +233,8 @@@ async def test_nft_mint_from_did_rpc
|
|
|
233
233
|
hex_did_id = did_wallet_maker.get_my_DID()
|
|
234
234
|
hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(config))
|
|
235
235
|
|
|
236
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_maker, timeout=20)
|
|
237
|
++
|
|
236
238
|
nft_wallet_maker = await api_maker.create_new_wallet(
|
|
237
239
|
dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)
|
|
238
240
|
)
|
|
@@@ -427,6 -427,6 +429,8 @@@ async def test_nft_mint_from_did_rpc_no
|
|
|
427
429
|
hex_did_id = did_wallet_maker.get_my_DID()
|
|
428
430
|
hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(config))
|
|
429
431
|
|
|
432
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_maker, timeout=20)
|
|
433
|
++
|
|
430
434
|
nft_wallet_maker = await api_maker.create_new_wallet(
|
|
431
435
|
dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)
|
|
432
436
|
)
|
|
@@@ -832,6 -832,6 +836,8 @@@ async def test_nft_mint_from_xch_rpc
|
|
|
832
836
|
hex_did_id = did_wallet_maker.get_my_DID()
|
|
833
837
|
hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), AddressType.DID.hrp(config))
|
|
834
838
|
|
|
839
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_maker, timeout=20)
|
|
840
|
++
|
|
835
841
|
nft_wallet_maker = await api_maker.create_new_wallet(
|
|
836
842
|
dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)
|
|
837
843
|
)
|
|
@@@ -905,3 -905,3 +905,276 @@@ async def test_nft_offer_nft_for_nft
|
|
|
905
905
|
|
|
906
906
|
assert await nft_wallet_maker.get_nft_count() == 1
|
|
907
907
|
assert await nft_wallet_taker.get_nft_count() == 1
|
|
908
|
++
|
|
909
|
++
|
|
910
|
++
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="save time")
|
|
911
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
912
|
++
@pytest.mark.parametrize("reuse_puzhash", [True, False])
|
|
913
|
++
@pytest.mark.anyio
|
|
914
|
++
async def test_nft_offer_nft0_and_xch_for_cat(
|
|
915
|
++
self_hostname: str,
|
|
916
|
++
two_wallet_nodes: Any,
|
|
917
|
++
trusted: Any,
|
|
918
|
++
reuse_puzhash: bool,
|
|
919
|
++
seeded_random: random.Random,
|
|
920
|
++
) -> None:
|
|
921
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
922
|
++
full_node_api: FullNodeSimulator = full_nodes[0]
|
|
923
|
++
full_node_server = full_node_api.server
|
|
924
|
++
wallet_node_0, server_0 = wallets[0]
|
|
925
|
++
wallet_node_1, server_1 = wallets[1]
|
|
926
|
++
wallet_maker = wallet_node_0.wallet_state_manager.main_wallet
|
|
927
|
++
wallet_taker = wallet_node_1.wallet_state_manager.main_wallet
|
|
928
|
++
|
|
929
|
++
maker_ph = await wallet_maker.get_new_puzzlehash()
|
|
930
|
++
taker_ph = await wallet_taker.get_new_puzzlehash()
|
|
931
|
++
token_ph = bytes32.random(seeded_random)
|
|
932
|
++
|
|
933
|
++
if trusted:
|
|
934
|
++
wallet_node_0.config["trusted_peers"] = {
|
|
935
|
++
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
|
|
936
|
++
}
|
|
937
|
++
wallet_node_1.config["trusted_peers"] = {
|
|
938
|
++
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
|
|
939
|
++
}
|
|
940
|
++
else:
|
|
941
|
++
wallet_node_0.config["trusted_peers"] = {}
|
|
942
|
++
wallet_node_1.config["trusted_peers"] = {}
|
|
943
|
++
|
|
944
|
++
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
945
|
++
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
946
|
++
|
|
947
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(maker_ph))
|
|
948
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(taker_ph))
|
|
949
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
|
|
950
|
++
|
|
951
|
++
funds = sum([calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, 2)])
|
|
952
|
++
|
|
953
|
++
await time_out_assert(20, wallet_maker.get_unconfirmed_balance, funds)
|
|
954
|
++
await time_out_assert(20, wallet_maker.get_confirmed_balance, funds)
|
|
955
|
++
|
|
956
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
|
|
957
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
|
|
958
|
++
|
|
959
|
++
tx_config = DEFAULT_TX_CONFIG.override(reuse_puzhash=reuse_puzhash)
|
|
960
|
++
|
|
961
|
++
# Create NFT wallets and nfts for maker and taker
|
|
962
|
++
nft_wallet_maker = await NFTWallet.create_new_nft_wallet(
|
|
963
|
++
wallet_node_0.wallet_state_manager, wallet_maker, name="NFT WALLET 1"
|
|
964
|
++
)
|
|
965
|
++
|
|
966
|
++
nft_wallet_taker = await NFTWallet.create_new_nft_wallet(
|
|
967
|
++
wallet_node_1.wallet_state_manager, wallet_taker, name="NFT WALLET 2"
|
|
968
|
++
)
|
|
969
|
++
|
|
970
|
++
trade_manager_maker = wallet_maker.wallet_state_manager.trade_manager
|
|
971
|
++
trade_manager_taker = wallet_taker.wallet_state_manager.trade_manager
|
|
972
|
++
|
|
973
|
++
metadata = Program.to(
|
|
974
|
++
[
|
|
975
|
++
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
|
976
|
++
("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
|
|
977
|
++
]
|
|
978
|
++
)
|
|
979
|
++
|
|
980
|
++
txs = await nft_wallet_maker.generate_new_nft(metadata, tx_config)
|
|
981
|
++
txs = await wallet_maker.wallet_state_manager.add_pending_transactions(txs)
|
|
982
|
++
for tx in txs:
|
|
983
|
++
if tx.spend_bundle is not None:
|
|
984
|
++
await time_out_assert_not_none(
|
|
985
|
++
20, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
|
|
986
|
++
)
|
|
987
|
++
|
|
988
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
|
|
989
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
|
|
990
|
++
|
|
991
|
++
coins_maker = await nft_wallet_maker.get_current_nfts()
|
|
992
|
++
assert len(coins_maker) == 1
|
|
993
|
++
assert await nft_wallet_taker.get_nft_count() == 0
|
|
994
|
++
# Create two new CATs and wallets for maker and taker
|
|
995
|
++
cats_to_mint = 10000
|
|
996
|
++
async with wallet_node_0.wallet_state_manager.lock:
|
|
997
|
++
cat_wallet_maker, _ = await CATWallet.create_new_cat_wallet(
|
|
998
|
++
wallet_node_0.wallet_state_manager,
|
|
999
|
++
wallet_maker,
|
|
1000
|
++
{"identifier": "genesis_by_id"},
|
|
1001
|
++
uint64(cats_to_mint),
|
|
1002
|
++
tx_config,
|
|
1003
|
++
)
|
|
1004
|
++
await time_out_assert(20, mempool_not_empty, True, full_node_api)
|
|
1005
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
|
|
1006
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
|
|
1007
|
++
|
|
1008
|
++
async with wallet_node_1.wallet_state_manager.lock:
|
|
1009
|
++
cat_wallet_taker, _ = await CATWallet.create_new_cat_wallet(
|
|
1010
|
++
wallet_node_1.wallet_state_manager,
|
|
1011
|
++
wallet_taker,
|
|
1012
|
++
{"identifier": "genesis_by_id"},
|
|
1013
|
++
uint64(cats_to_mint),
|
|
1014
|
++
tx_config,
|
|
1015
|
++
)
|
|
1016
|
++
await time_out_assert(20, mempool_not_empty, True, full_node_api)
|
|
1017
|
++
|
|
1018
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
|
|
1019
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
|
|
1020
|
++
|
|
1021
|
++
await time_out_assert(20, cat_wallet_maker.get_confirmed_balance, cats_to_mint)
|
|
1022
|
++
await time_out_assert(20, cat_wallet_maker.get_unconfirmed_balance, cats_to_mint)
|
|
1023
|
++
await time_out_assert(20, cat_wallet_taker.get_confirmed_balance, cats_to_mint)
|
|
1024
|
++
await time_out_assert(20, cat_wallet_taker.get_unconfirmed_balance, cats_to_mint)
|
|
1025
|
++
|
|
1026
|
++
wallet_maker_for_taker_cat: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
|
|
1027
|
++
wallet_node_0.wallet_state_manager, wallet_maker, cat_wallet_taker.get_asset_id()
|
|
1028
|
++
)
|
|
1029
|
++
|
|
1030
|
++
wallet_taker_for_maker_cat: CATWallet = await CATWallet.get_or_create_wallet_for_cat(
|
|
1031
|
++
wallet_node_1.wallet_state_manager, wallet_taker, cat_wallet_maker.get_asset_id()
|
|
1032
|
++
)
|
|
1033
|
++
|
|
1034
|
++
assert wallet_taker_for_maker_cat
|
|
1035
|
++
# MAKE FIRST TRADE: 1 NFT for 10 taker cats
|
|
1036
|
++
maker_balance_pre = await wallet_maker.get_confirmed_balance()
|
|
1037
|
++
taker_balance_pre = await wallet_taker.get_confirmed_balance()
|
|
1038
|
++
taker_cat_maker_balance_pre = await wallet_maker_for_taker_cat.get_confirmed_balance()
|
|
1039
|
++
taker_cat_taker_balance_pre = await cat_wallet_taker.get_confirmed_balance()
|
|
1040
|
++
|
|
1041
|
++
nft_to_offer = coins_maker[0]
|
|
1042
|
++
nft_info: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(nft_to_offer.full_puzzle))
|
|
1043
|
++
nft_asset_id: bytes32 = create_asset_id(nft_info) # type: ignore
|
|
1044
|
++
driver_dict: Dict[bytes32, Optional[PuzzleInfo]] = {nft_asset_id: nft_info}
|
|
1045
|
++
|
|
1046
|
++
maker_fee = uint64(10)
|
|
1047
|
++
maker_xch_offered = 1000
|
|
1048
|
++
taker_cat_offered = 2500
|
|
1049
|
++
wallet_maker_id = wallet_maker.id()
|
|
1050
|
++
offer_nft_for_cat = {
|
|
1051
|
++
wallet_maker_id: -maker_xch_offered,
|
|
1052
|
++
nft_asset_id: -1,
|
|
1053
|
++
wallet_maker_for_taker_cat.id(): taker_cat_offered,
|
|
1054
|
++
}
|
|
1055
|
++
maker_unused_index = (
|
|
1056
|
++
await wallet_maker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
|
|
1057
|
++
).index
|
|
1058
|
++
taker_unused_index = (
|
|
1059
|
++
await wallet_taker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
|
|
1060
|
++
).index
|
|
1061
|
++
|
|
1062
|
++
success, trade_make, _, error = await trade_manager_maker.create_offer_for_ids(
|
|
1063
|
++
offer_nft_for_cat, tx_config, driver_dict, fee=maker_fee
|
|
1064
|
++
)
|
|
1065
|
++
assert success is True
|
|
1066
|
++
assert error is None
|
|
1067
|
++
assert trade_make is not None
|
|
1068
|
++
|
|
1069
|
++
taker_fee = uint64(1)
|
|
1070
|
++
|
|
1071
|
++
peer = wallet_node_1.get_full_node_peer()
|
|
1072
|
++
trade_take, tx_records = await trade_manager_taker.respond_to_offer(
|
|
1073
|
++
Offer.from_bytes(trade_make.offer),
|
|
1074
|
++
peer,
|
|
1075
|
++
tx_config,
|
|
1076
|
++
fee=taker_fee,
|
|
1077
|
++
)
|
|
1078
|
++
|
|
1079
|
++
assert trade_take is not None
|
|
1080
|
++
assert tx_records is not None
|
|
1081
|
++
|
|
1082
|
++
tx_records = await trade_manager_taker.wallet_state_manager.add_pending_transactions(tx_records)
|
|
1083
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
1084
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
|
|
1085
|
++
|
|
1086
|
++
await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_maker, trade_make)
|
|
1087
|
++
await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_taker, trade_take)
|
|
1088
|
++
|
|
1089
|
++
taker_cat_maker_balance_post = await wallet_maker_for_taker_cat.get_confirmed_balance()
|
|
1090
|
++
taker_cat_taker_balance_post = await cat_wallet_taker.get_confirmed_balance()
|
|
1091
|
++
assert taker_cat_maker_balance_post == taker_cat_maker_balance_pre + taker_cat_offered
|
|
1092
|
++
assert taker_cat_taker_balance_post == taker_cat_taker_balance_pre - taker_cat_offered
|
|
1093
|
++
maker_balance_post = await wallet_maker.get_confirmed_balance()
|
|
1094
|
++
taker_balance_post = await wallet_taker.get_confirmed_balance()
|
|
1095
|
++
assert maker_balance_post == maker_balance_pre - maker_fee - maker_xch_offered
|
|
1096
|
++
assert taker_balance_post == taker_balance_pre - taker_fee + maker_xch_offered
|
|
1097
|
++
coins_taker = await nft_wallet_taker.get_current_nfts()
|
|
1098
|
++
assert len(coins_taker) == 1
|
|
1099
|
++
|
|
1100
|
++
assert await nft_wallet_maker.get_nft_count() == 0
|
|
1101
|
++
if reuse_puzhash:
|
|
1102
|
++
# Check if unused index changed
|
|
1103
|
++
assert (
|
|
1104
|
++
maker_unused_index
|
|
1105
|
++
== (
|
|
1106
|
++
await wallet_maker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
|
|
1107
|
++
).index
|
|
1108
|
++
)
|
|
1109
|
++
assert (
|
|
1110
|
++
taker_unused_index
|
|
1111
|
++
== (
|
|
1112
|
++
await wallet_taker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
|
|
1113
|
++
).index
|
|
1114
|
++
)
|
|
1115
|
++
else:
|
|
1116
|
++
assert (
|
|
1117
|
++
maker_unused_index
|
|
1118
|
++
< (
|
|
1119
|
++
await wallet_maker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
|
|
1120
|
++
).index
|
|
1121
|
++
)
|
|
1122
|
++
assert (
|
|
1123
|
++
taker_unused_index
|
|
1124
|
++
< (
|
|
1125
|
++
await wallet_taker.wallet_state_manager.puzzle_store.get_current_derivation_record_for_wallet(uint32(1))
|
|
1126
|
++
).index
|
|
1127
|
++
)
|
|
1128
|
++
# Make an offer for taker NFT for multiple cats
|
|
1129
|
++
maker_cat_amount = 400
|
|
1130
|
++
taker_cat_amount = 500
|
|
1131
|
++
|
|
1132
|
++
nft_to_buy = coins_taker[0]
|
|
1133
|
++
nft_to_buy_info: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(nft_to_buy.full_puzzle))
|
|
1134
|
++
nft_to_buy_asset_id: bytes32 = create_asset_id(nft_to_buy_info) # type: ignore
|
|
1135
|
++
|
|
1136
|
++
driver_dict_to_buy: Dict[bytes32, Optional[PuzzleInfo]] = {
|
|
1137
|
++
nft_to_buy_asset_id: nft_to_buy_info,
|
|
1138
|
++
}
|
|
1139
|
++
|
|
1140
|
++
maker_fee = uint64(10)
|
|
1141
|
++
offer_multi_cats_for_nft = {
|
|
1142
|
++
nft_to_buy_asset_id: 1,
|
|
1143
|
++
wallet_maker_for_taker_cat.id(): -taker_cat_amount,
|
|
1144
|
++
cat_wallet_maker.id(): -maker_cat_amount,
|
|
1145
|
++
}
|
|
1146
|
++
|
|
1147
|
++
success, trade_make, _, error = await trade_manager_maker.create_offer_for_ids(
|
|
1148
|
++
offer_multi_cats_for_nft, tx_config, driver_dict_to_buy, fee=maker_fee
|
|
1149
|
++
)
|
|
1150
|
++
assert success is True
|
|
1151
|
++
assert error is None
|
|
1152
|
++
assert trade_make is not None
|
|
1153
|
++
|
|
1154
|
++
taker_fee = uint64(1)
|
|
1155
|
++
|
|
1156
|
++
trade_take, tx_records = await trade_manager_taker.respond_to_offer(
|
|
1157
|
++
Offer.from_bytes(trade_make.offer), peer, tx_config, fee=taker_fee
|
|
1158
|
++
)
|
|
1159
|
++
|
|
1160
|
++
assert trade_take is not None
|
|
1161
|
++
assert tx_records is not None
|
|
1162
|
++
|
|
1163
|
++
tx_records = await trade_manager_taker.wallet_state_manager.add_pending_transactions(tx_records)
|
|
1164
|
++
await full_node_api.process_transaction_records(records=tx_records)
|
|
1165
|
++
# check balances: taker wallet down an NFT, up cats
|
|
1166
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1], timeout=20)
|
|
1167
|
++
|
|
1168
|
++
await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_maker, trade_make)
|
|
1169
|
++
await time_out_assert(20, get_trade_and_status, TradeStatus.CONFIRMED, trade_manager_taker, trade_take)
|
|
1170
|
++
|
|
1171
|
++
taker_cat_maker_balance_post_2 = await wallet_maker_for_taker_cat.get_confirmed_balance()
|
|
1172
|
++
taker_cat_taker_balance_post_2 = await cat_wallet_taker.get_confirmed_balance()
|
|
1173
|
++
assert taker_cat_maker_balance_post_2 == taker_cat_maker_balance_post - taker_cat_amount
|
|
1174
|
++
assert taker_cat_taker_balance_post_2 == taker_cat_taker_balance_post + taker_cat_amount
|
|
1175
|
++
maker_balance_post_2 = await wallet_maker.get_confirmed_balance()
|
|
1176
|
++
taker_balance_post_2 = await wallet_taker.get_confirmed_balance()
|
|
1177
|
++
assert maker_balance_post_2 == maker_balance_post - maker_fee
|
|
1178
|
++
assert taker_balance_post_2 == taker_balance_post - taker_fee
|
|
1179
|
++
assert await nft_wallet_maker.get_nft_count() == 1
|
|
1180
|
++
assert await nft_wallet_taker.get_nft_count() == 0
|
|
@@@ -1,6 -1,6 +1,7 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
++
import sys
|
|
4
5
|
import time
|
|
5
6
|
from typing import Any, Awaitable, Callable, Dict, List, Optional
|
|
6
7
|
|
|
@@@ -22,6 -22,6 +23,7 @@@ from chia.util.byte_types import hexstr
|
|
|
22
23
|
from chia.util.ints import uint32, uint64
|
|
23
24
|
from chia.util.timing import adjusted_timeout
|
|
24
25
|
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
|
26
|
++
from chia.wallet.nft_wallet.nft_info import NFTInfo
|
|
25
27
|
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
|
|
26
28
|
from chia.wallet.transaction_record import TransactionRecord
|
|
27
29
|
from chia.wallet.util.address_type import AddressType
|
|
@@@ -31,6 -31,6 +33,7 @@@ from chia.wallet.util.wallet_types impo
|
|
|
31
33
|
from chia.wallet.wallet_node import WalletNode
|
|
32
34
|
from chia.wallet.wallet_state_manager import WalletStateManager
|
|
33
35
|
from tests.conftest import ConsensusMode
|
|
36
|
++
from tests.util.setup_nodes import OldSimulatorsAndWallets
|
|
34
37
|
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
|
|
35
38
|
|
|
36
39
|
|
|
@@@ -44,10 -44,10 +47,10 @@@ async def get_wallet_number(manager: Wa
|
|
|
44
47
|
|
|
45
48
|
async def wait_rpc_state_condition(
|
|
46
49
|
timeout: float,
|
|
47
|
--
async_function: Callable[[Dict[str, Any]], Awaitable[Dict]],
|
|
48
|
--
params: List[Dict],
|
|
50
|
++
async_function: Callable[[Dict[str, Any]], Awaitable[Dict[str, Any]]],
|
|
51
|
++
params: List[Dict[str, Any]],
|
|
49
52
|
condition_func: Callable[[Dict[str, Any]], bool],
|
|
50
|
--
) -> Dict:
|
|
53
|
++
) -> Dict[str, Any]:
|
|
51
54
|
__tracebackhide__ = True
|
|
52
55
|
|
|
53
56
|
timeout = adjusted_timeout(timeout=timeout)
|
|
@@@ -71,7 -71,7 +74,7 @@@
|
|
|
71
74
|
|
|
72
75
|
|
|
73
76
|
async def make_new_block_with(
|
|
74
|
--
resp: Dict, full_node_api: FullNodeSimulator, ph: bytes32, node_to_sync: Optional[WalletNode] = None
|
|
77
|
++
resp: Dict[str, Any], full_node_api: FullNodeSimulator, ph: bytes32, node_to_sync: Optional[WalletNode] = None
|
|
75
78
|
) -> SpendBundle:
|
|
76
79
|
assert resp.get("success")
|
|
77
80
|
sb = resp["spend_bundle"]
|
|
@@@ -83,12 -83,12 +86,11 @@@
|
|
|
83
86
|
return sb
|
|
84
87
|
|
|
85
88
|
|
|
86
|
--
@pytest.mark.parametrize(
|
|
87
|
--
"trusted",
|
|
88
|
--
[True, False],
|
|
89
|
--
)
|
|
89
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
90
90
|
@pytest.mark.anyio
|
|
91
|
--
async def test_nft_wallet_creation_automatically(
|
|
91
|
++
async def test_nft_wallet_creation_automatically(
|
|
92
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
93
|
++
) -> None:
|
|
92
94
|
num_blocks = 3
|
|
93
95
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
94
96
|
full_node_api = full_nodes[0]
|
|
@@@ -115,7 -115,7 +117,7 @@@
|
|
|
115
117
|
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
116
118
|
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
117
119
|
|
|
118
|
--
for
|
|
120
|
++
for _ in range(1, num_blocks):
|
|
119
121
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
120
122
|
|
|
121
123
|
funds = sum(
|
|
@@@ -124,10 -124,10 +126,10 @@@
|
|
|
124
126
|
|
|
125
127
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
126
128
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
127
|
--
for
|
|
129
|
++
for _ in range(1, num_blocks):
|
|
128
130
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
129
131
|
|
|
130
|
--
for
|
|
132
|
++
for _ in range(1, num_blocks):
|
|
131
133
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
132
134
|
|
|
133
135
|
await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
|
|
@@@ -135,21 -135,21 +137,18 @@@
|
|
|
135
137
|
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
|
|
136
138
|
)
|
|
137
139
|
metadata = Program.to(
|
|
138
|
--
[
|
|
139
|
--
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
|
140
|
--
("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
|
|
141
|
--
]
|
|
140
|
++
[("u", ["https://www.chia.net/img/branding/chia-logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F185")]
|
|
142
141
|
)
|
|
143
142
|
|
|
144
143
|
txs = await nft_wallet_0.generate_new_nft(metadata, DEFAULT_TX_CONFIG)
|
|
145
|
--
await nft_wallet_0.wallet_state_manager.add_pending_transactions(txs)
|
|
144
|
++
txs = await nft_wallet_0.wallet_state_manager.add_pending_transactions(txs)
|
|
146
145
|
for tx in txs:
|
|
147
146
|
if tx.spend_bundle is not None:
|
|
148
147
|
await time_out_assert_not_none(
|
|
149
148
|
30, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
|
|
150
149
|
)
|
|
151
150
|
|
|
152
|
--
for
|
|
151
|
++
for _ in range(1, num_blocks):
|
|
153
152
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
154
153
|
|
|
155
154
|
await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
|
|
@@@ -165,7 -165,7 +164,7 @@@
|
|
|
165
164
|
await time_out_assert_not_none(
|
|
166
165
|
30, full_node_api.full_node.mempool_manager.get_spendbundle, txs[0].spend_bundle.name()
|
|
167
166
|
)
|
|
168
|
--
for
|
|
167
|
++
for _ in range(1, num_blocks):
|
|
169
168
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
170
169
|
|
|
171
170
|
async def num_wallets() -> int:
|
|
@@@ -175,7 -175,7 +174,8 @@@
|
|
|
175
174
|
# Get the new NFT wallet
|
|
176
175
|
nft_wallets = await wallet_node_1.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
|
|
177
176
|
assert len(nft_wallets) == 1
|
|
178
|
--
nft_wallet_1
|
|
177
|
++
nft_wallet_1 = wallet_node_1.wallet_state_manager.wallets[nft_wallets[0].id]
|
|
178
|
++
assert isinstance(nft_wallet_1, NFTWallet)
|
|
179
179
|
await time_out_assert(30, get_nft_count, 0, nft_wallet_0)
|
|
180
180
|
await time_out_assert(30, get_nft_count, 1, nft_wallet_1)
|
|
181
181
|
|
|
@@@ -183,13 -183,13 +183,16 @@@
|
|
|
183
183
|
assert await nft_wallet_1.get_nft_count() == 1
|
|
184
184
|
|
|
185
185
|
|
|
186
|
++
@pytest.mark.skipif(sys.platform == "win32" and sys.version_info < (3, 9), reason="Flaky on Windows+3.8")
|
|
186
187
|
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
|
187
188
|
@pytest.mark.parametrize("trusted", [True, False])
|
|
188
189
|
@pytest.mark.anyio
|
|
189
|
--
async def test_nft_wallet_creation_and_transfer(
|
|
190
|
++
async def test_nft_wallet_creation_and_transfer(
|
|
191
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
192
|
++
) -> None:
|
|
190
193
|
num_blocks = 2
|
|
191
194
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
192
|
--
full_node_api
|
|
195
|
++
full_node_api = full_nodes[0]
|
|
193
196
|
full_node_server = full_node_api.server
|
|
194
197
|
wallet_node_0, server_0 = wallets[0]
|
|
195
198
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -198,6 -198,6 +201,10 @@@
|
|
|
198
201
|
ph = await wallet_0.get_new_puzzlehash()
|
|
199
202
|
ph1 = await wallet_1.get_new_puzzlehash()
|
|
200
203
|
|
|
204
|
++
async def ensure_wallet_sync() -> None:
|
|
205
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
|
|
206
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_1, timeout=20)
|
|
207
|
++
|
|
201
208
|
if trusted:
|
|
202
209
|
wallet_node_0.config["trusted_peers"] = {
|
|
203
210
|
full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex()
|
|
@@@ -212,7 -212,7 +219,7 @@@
|
|
|
212
219
|
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
213
220
|
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
214
221
|
|
|
215
|
--
for
|
|
222
|
++
for _ in range(1, num_blocks):
|
|
216
223
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
217
224
|
|
|
218
225
|
funds = sum(
|
|
@@@ -221,10 -221,10 +228,10 @@@
|
|
|
221
228
|
|
|
222
229
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
223
230
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
224
|
--
for
|
|
231
|
++
for _ in range(1, num_blocks):
|
|
225
232
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
226
233
|
|
|
227
|
--
for
|
|
234
|
++
for _ in range(1, num_blocks):
|
|
228
235
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
229
236
|
|
|
230
237
|
await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
|
|
@@@ -232,10 -232,10 +239,7 @@@
|
|
|
232
239
|
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
|
|
233
240
|
)
|
|
234
241
|
metadata = Program.to(
|
|
235
|
--
[
|
|
236
|
--
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
|
237
|
--
("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
|
|
238
|
--
]
|
|
242
|
++
[("u", ["https://www.chia.net/img/branding/chia-logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F185")]
|
|
239
243
|
)
|
|
240
244
|
|
|
241
245
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 2000000000000)
|
|
@@@ -249,7 -249,7 +253,7 @@@
|
|
|
249
253
|
30, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
|
|
250
254
|
)
|
|
251
255
|
|
|
252
|
--
for
|
|
256
|
++
for _ in range(1, num_blocks):
|
|
253
257
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
254
258
|
|
|
255
259
|
await time_out_assert(10, get_nft_count, 1, nft_wallet_0)
|
|
@@@ -258,7 -258,7 +262,11 @@@
|
|
|
258
262
|
# Test Reorg mint
|
|
259
263
|
height = full_node_api.full_node.blockchain.get_peak_height()
|
|
260
264
|
assert height is not None
|
|
265
|
++
|
|
261
266
|
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(uint32(height - 1), uint32(height + 1), ph1, None))
|
|
267
|
++
|
|
268
|
++
await time_out_assert(60, full_node_api.full_node.blockchain.get_peak_height, height + 1)
|
|
269
|
++
|
|
262
270
|
await time_out_assert(30, get_nft_count, 0, nft_wallet_0)
|
|
263
271
|
await time_out_assert(30, get_wallet_number, 2, wallet_node_0.wallet_state_manager)
|
|
264
272
|
|
|
@@@ -266,12 -266,12 +274,7 @@@
|
|
|
266
274
|
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET 1"
|
|
267
275
|
)
|
|
268
276
|
|
|
269
|
--
metadata = Program.to(
|
|
270
|
--
[
|
|
271
|
--
("u", ["https://www.test.net/logo.svg"]),
|
|
272
|
--
("h", "0xD4584AD463139FA8C0D9F68F4B59F181"),
|
|
273
|
--
]
|
|
274
|
--
)
|
|
277
|
++
metadata = Program.to([("u", ["https://www.test.net/logo.svg"]), ("h", "0xD4584AD463139FA8C0D9F68F4B59F181")])
|
|
275
278
|
|
|
276
279
|
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 4000000000000 - 1)
|
|
277
280
|
await time_out_assert(10, wallet_0.get_confirmed_balance, 4000000000000)
|
|
@@@ -286,8 -286,8 +289,9 @@@
|
|
|
286
289
|
)
|
|
287
290
|
|
|
288
291
|
await time_out_assert(30, wallet_node_0.wallet_state_manager.lock.locked, False)
|
|
289
|
--
for
|
|
292
|
++
for _ in range(1, num_blocks * 2):
|
|
290
293
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
294
|
++
|
|
291
295
|
await time_out_assert(30, get_nft_count, 2, nft_wallet_0)
|
|
292
296
|
coins = await nft_wallet_0.get_current_nfts()
|
|
293
297
|
assert len(coins) == 2, "nft not generated"
|
|
@@@ -307,8 -307,8 +311,11 @@@
|
|
|
307
311
|
)
|
|
308
312
|
assert len(compute_memos(txs[0].spend_bundle)) > 0
|
|
309
313
|
|
|
310
|
--
for
|
|
314
|
++
for _ in range(1, num_blocks * 2):
|
|
311
315
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
316
|
++
|
|
317
|
++
await ensure_wallet_sync()
|
|
318
|
++
|
|
312
319
|
await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
|
|
313
320
|
await time_out_assert(30, get_nft_count, 1, nft_wallet_1)
|
|
314
321
|
|
|
@@@ -329,27 -329,27 +336,37 @@@
|
|
|
329
336
|
)
|
|
330
337
|
assert len(compute_memos(txs[0].spend_bundle)) > 0
|
|
331
338
|
|
|
332
|
--
|
|
339
|
++
await ensure_wallet_sync()
|
|
340
|
++
|
|
341
|
++
# We wish for the wallet to be in a specific state at this point after having
|
|
342
|
++
# become synced above.
|
|
343
|
++
for _ in range(1, num_blocks):
|
|
333
344
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
334
345
|
|
|
335
|
--
await
|
|
346
|
++
await ensure_wallet_sync()
|
|
347
|
++
|
|
348
|
++
# The wallets are locked so we still observe the conditions above.
|
|
336
349
|
await time_out_assert(30, get_nft_count, 2, nft_wallet_0)
|
|
337
350
|
await time_out_assert(30, get_nft_count, 0, nft_wallet_1)
|
|
338
351
|
|
|
339
352
|
# Test Reorg
|
|
340
353
|
height = full_node_api.full_node.blockchain.get_peak_height()
|
|
354
|
++
|
|
341
355
|
assert height is not None
|
|
342
356
|
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(uint32(height - 1), uint32(height + 2), ph1, None))
|
|
357
|
++
|
|
358
|
++
# Wait for wallet sync on all wallets.
|
|
359
|
++
await ensure_wallet_sync()
|
|
360
|
++
|
|
343
361
|
await time_out_assert(30, get_nft_count, 1, nft_wallet_0)
|
|
344
362
|
await time_out_assert(30, get_nft_count, 1, nft_wallet_1)
|
|
345
363
|
|
|
346
364
|
|
|
347
|
--
@pytest.mark.parametrize(
|
|
348
|
--
"trusted",
|
|
349
|
--
[True, False],
|
|
350
|
--
)
|
|
365
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
351
366
|
@pytest.mark.anyio
|
|
352
|
--
async def test_nft_wallet_rpc_creation_and_list(
|
|
367
|
++
async def test_nft_wallet_rpc_creation_and_list(
|
|
368
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
369
|
++
) -> None:
|
|
353
370
|
num_blocks = 3
|
|
354
371
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
355
372
|
full_node_api = full_nodes[0]
|
|
@@@ -376,7 -376,7 +393,7 @@@
|
|
|
376
393
|
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
377
394
|
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
378
395
|
|
|
379
|
--
for
|
|
396
|
++
for _ in range(1, num_blocks):
|
|
380
397
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
381
398
|
funds = sum(
|
|
382
399
|
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks - 1)]
|
|
@@@ -407,7 -407,7 +424,7 @@@
|
|
|
407
424
|
sb = tr1["spend_bundle"]
|
|
408
425
|
|
|
409
426
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
410
|
--
for
|
|
427
|
++
for _ in range(1, num_blocks):
|
|
411
428
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
412
429
|
|
|
413
430
|
await wait_rpc_state_condition(30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"])
|
|
@@@ -427,15 -427,15 +444,12 @@@
|
|
|
427
444
|
assert tr2.get("success")
|
|
428
445
|
sb = tr2["spend_bundle"]
|
|
429
446
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
430
|
--
for
|
|
447
|
++
for _ in range(1, num_blocks):
|
|
431
448
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
432
449
|
coins_response = await wait_rpc_state_condition(
|
|
433
|
--
5,
|
|
434
|
--
api_0.nft_get_nfts,
|
|
435
|
--
[{"wallet_id": nft_wallet_0_id}],
|
|
436
|
--
lambda x: x["success"] and len(x["nft_list"]) == 2,
|
|
450
|
++
5, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: x["success"] and len(x["nft_list"]) == 2
|
|
437
451
|
)
|
|
438
|
--
coins = coins_response["nft_list"]
|
|
452
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
439
453
|
uris = []
|
|
440
454
|
for coin in coins:
|
|
441
455
|
assert not coin.supports_did
|
|
@@@ -469,12 -469,12 +483,11 @@@
|
|
|
469
483
|
assert resp.get("count") is None
|
|
470
484
|
|
|
471
485
|
|
|
472
|
--
@pytest.mark.parametrize(
|
|
473
|
--
"trusted",
|
|
474
|
--
[True, False],
|
|
475
|
--
)
|
|
486
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
476
487
|
@pytest.mark.anyio
|
|
477
|
--
async def test_nft_wallet_rpc_update_metadata(
|
|
488
|
++
async def test_nft_wallet_rpc_update_metadata(
|
|
489
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
490
|
++
) -> None:
|
|
478
491
|
num_blocks = 3
|
|
479
492
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
480
493
|
full_node_api = full_nodes[0]
|
|
@@@ -498,7 -498,7 +511,7 @@@
|
|
|
498
511
|
wallet_node_0.config["trusted_peers"] = {}
|
|
499
512
|
wallet_node_1.config["trusted_peers"] = {}
|
|
500
513
|
|
|
501
|
--
for
|
|
514
|
++
for _ in range(1, num_blocks):
|
|
502
515
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
503
516
|
|
|
504
517
|
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
@@@ -535,14 -535,14 +548,14 @@@
|
|
|
535
548
|
sb = resp["spend_bundle"]
|
|
536
549
|
|
|
537
550
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
538
|
--
for
|
|
551
|
++
for _ in range(1, num_blocks):
|
|
539
552
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
540
553
|
coins_response = await wait_rpc_state_condition(
|
|
541
554
|
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
|
|
542
555
|
)
|
|
543
556
|
assert coins_response["nft_list"], isinstance(coins_response, dict)
|
|
544
557
|
assert coins_response.get("success")
|
|
545
|
--
coins = coins_response["nft_list"]
|
|
558
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
546
559
|
coin = coins[0].to_json_dict()
|
|
547
560
|
assert coin["mint_height"] > 0
|
|
548
561
|
assert coin["data_hash"] == "0xd4584ad463139fa8c0d9f68f4b59f185"
|
|
@@@ -567,13 -567,13 +580,14 @@@
|
|
|
567
580
|
{"wallet_id": nft_wallet_0_id, "nft_coin_id": nft_coin_id, "uri": "http://metadata", "key": "mu"}
|
|
568
581
|
)
|
|
569
582
|
|
|
570
|
--
assert isinstance(tr1, dict)
|
|
571
583
|
assert tr1.get("success")
|
|
572
584
|
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
|
|
573
|
--
|
|
585
|
++
coins = coins_response["nft_list"]
|
|
586
|
++
assert coins[0].pending_transaction
|
|
574
587
|
sb = tr1["spend_bundle"]
|
|
588
|
++
assert isinstance(sb, SpendBundle)
|
|
575
589
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
576
|
--
for
|
|
590
|
++
for _ in range(1, num_blocks):
|
|
577
591
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
578
592
|
# check that new URI was added
|
|
579
593
|
coins_response = await wait_rpc_state_condition(
|
|
@@@ -597,19 -597,19 +611,14 @@@
|
|
|
597
611
|
await time_out_assert(30, wallet_0.get_pending_change_balance, 0)
|
|
598
612
|
nft_coin_id = coin["nft_coin_id"]
|
|
599
613
|
tr1 = await api_0.nft_add_uri(
|
|
600
|
--
{
|
|
601
|
--
"wallet_id": nft_wallet_0_id,
|
|
602
|
--
"nft_coin_id": nft_coin_id,
|
|
603
|
--
"uri": "http://data",
|
|
604
|
--
"key": "u",
|
|
605
|
--
}
|
|
614
|
++
{"wallet_id": nft_wallet_0_id, "nft_coin_id": nft_coin_id, "uri": "http://data", "key": "u"}
|
|
606
615
|
)
|
|
607
616
|
|
|
608
617
|
assert isinstance(tr1, dict)
|
|
609
618
|
assert tr1.get("success")
|
|
610
619
|
sb = tr1["spend_bundle"]
|
|
611
620
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
612
|
--
for
|
|
621
|
++
for _ in range(1, num_blocks):
|
|
613
622
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
614
623
|
coins_response = await wait_rpc_state_condition(
|
|
615
624
|
5,
|
|
@@@ -627,15 -627,15 +636,14 @@@
|
|
|
627
636
|
assert "http://data" == coin["data_uris"][0]
|
|
628
637
|
|
|
629
638
|
|
|
630
|
--
@pytest.mark.parametrize(
|
|
631
|
--
"trusted",
|
|
632
|
--
[True, False],
|
|
633
|
--
)
|
|
639
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
634
640
|
@pytest.mark.anyio
|
|
635
|
--
async def test_nft_with_did_wallet_creation(
|
|
641
|
++
async def test_nft_with_did_wallet_creation(
|
|
642
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
643
|
++
) -> None:
|
|
636
644
|
num_blocks = 3
|
|
637
645
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
638
|
--
full_node_api
|
|
646
|
++
full_node_api = full_nodes[0]
|
|
639
647
|
full_node_server = full_node_api.server
|
|
640
648
|
wallet_node_0, server_0 = wallets[0]
|
|
641
649
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -666,12 -666,12 +674,10 @@@
|
|
|
666
674
|
|
|
667
675
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
668
676
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
669
|
--
did_wallet
|
|
670
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
671
|
--
)
|
|
677
|
++
did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
672
678
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
|
673
|
--
|
|
674
679
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
680
|
++
assert spend_bundle is not None
|
|
675
681
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
676
682
|
|
|
677
683
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
@@@ -715,7 -715,7 +721,7 @@@
|
|
|
715
721
|
assert res.get("did_id") == hmr_did_id
|
|
716
722
|
|
|
717
723
|
# Create a NFT with DID
|
|
718
|
--
nft_ph
|
|
724
|
++
nft_ph = await wallet_0.get_new_puzzlehash()
|
|
719
725
|
resp = await api_0.nft_mint_nft(
|
|
720
726
|
{
|
|
721
727
|
"wallet_id": nft_wallet_0_id,
|
|
@@@ -768,7 -768,7 +774,7 @@@
|
|
|
768
774
|
coins_response = await wait_rpc_state_condition(
|
|
769
775
|
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
|
|
770
776
|
)
|
|
771
|
--
coins = coins_response["nft_list"]
|
|
777
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
772
778
|
assert len(coins) == 1
|
|
773
779
|
did_nft = coins[0].to_json_dict()
|
|
774
780
|
assert did_nft["mint_height"] > 0
|
|
@@@ -794,15 -794,15 +800,12 @@@
|
|
|
794
800
|
assert non_did_nft["owner_did"] is None
|
|
795
801
|
|
|
796
802
|
|
|
797
|
--
@pytest.mark.parametrize(
|
|
798
|
--
"trusted",
|
|
799
|
--
[True, False],
|
|
800
|
--
)
|
|
803
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
801
804
|
@pytest.mark.anyio
|
|
802
|
--
async def test_nft_rpc_mint(self_hostname: str, two_wallet_nodes:
|
|
805
|
++
async def test_nft_rpc_mint(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
803
806
|
num_blocks = 3
|
|
804
807
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
805
|
--
full_node_api
|
|
808
|
++
full_node_api = full_nodes[0]
|
|
806
809
|
full_node_server = full_node_api.server
|
|
807
810
|
wallet_node_0, server_0 = wallets[0]
|
|
808
811
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -835,12 -835,12 +838,10 @@@
|
|
|
835
838
|
|
|
836
839
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
837
840
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
838
|
--
did_wallet
|
|
839
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
840
|
--
)
|
|
841
|
++
did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
841
842
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
|
842
|
--
|
|
843
843
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
844
|
++
assert spend_bundle is not None
|
|
844
845
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
845
846
|
|
|
846
847
|
for _ in range(1, num_blocks):
|
|
@@@ -884,21 -884,21 +885,21 @@@
|
|
|
884
885
|
}
|
|
885
886
|
)
|
|
886
887
|
assert resp.get("success")
|
|
887
|
--
nft_id
|
|
888
|
++
nft_id = str(resp.get("nft_id"))
|
|
888
889
|
sb = resp["spend_bundle"]
|
|
889
890
|
|
|
890
891
|
# ensure hints are generated
|
|
891
892
|
assert len(compute_memos(sb)) > 0
|
|
892
893
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
893
894
|
|
|
894
|
--
for
|
|
895
|
++
for _ in range(1, num_blocks):
|
|
895
896
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
896
897
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 9999999999998)
|
|
897
898
|
await time_out_assert(30, wallet_0.get_confirmed_balance, 9999999999998)
|
|
898
899
|
coins_response = await wait_rpc_state_condition(
|
|
899
900
|
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
|
|
900
901
|
)
|
|
901
|
--
coins = coins_response["nft_list"]
|
|
902
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
902
903
|
assert len(coins) == 1
|
|
903
904
|
did_nft = coins[0]
|
|
904
905
|
assert did_nft.royalty_puzzle_hash == royalty_address
|
|
@@@ -913,16 -913,16 +914,15 @@@
|
|
|
913
914
|
assert decode_puzzle_hash(nft_id) == did_nft.launcher_id
|
|
914
915
|
|
|
915
916
|
|
|
916
|
--
@pytest.mark.parametrize(
|
|
917
|
--
"trusted",
|
|
918
|
--
[True, False],
|
|
919
|
--
)
|
|
917
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
920
918
|
@pytest.mark.anyio
|
|
921
|
--
async def test_nft_transfer_nft_with_did(
|
|
919
|
++
async def test_nft_transfer_nft_with_did(
|
|
920
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
921
|
++
) -> None:
|
|
922
922
|
num_blocks = 3
|
|
923
923
|
fee = 100
|
|
924
924
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
925
|
--
full_node_api
|
|
925
|
++
full_node_api = full_nodes[0]
|
|
926
926
|
full_node_server = full_node_api.server
|
|
927
927
|
wallet_node_0, server_0 = wallets[0]
|
|
928
928
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -958,12 -958,12 +958,10 @@@
|
|
|
958
958
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
959
959
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
960
960
|
# Create DID
|
|
961
|
--
did_wallet
|
|
962
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
963
|
--
)
|
|
961
|
++
did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
964
962
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
|
965
|
--
|
|
966
963
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
964
|
++
assert spend_bundle is not None
|
|
967
965
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
968
966
|
|
|
969
967
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
@@@ -1000,8 -1000,8 +998,9 @@@
|
|
|
1000
998
|
)
|
|
1001
999
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 5999999999898)
|
|
1002
1000
|
await time_out_assert(30, wallet_0.get_confirmed_balance, 5999999999898)
|
|
1003
|
--
coins = coins_response["nft_list"]
|
|
1001
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
1004
1002
|
assert len(coins) == 1
|
|
1003
|
++
assert coins[0].owner_did is not None
|
|
1005
1004
|
assert coins[0].owner_did.hex() == hex_did_id
|
|
1006
1005
|
|
|
1007
1006
|
assert len(wallet_1.wallet_state_manager.wallets) == 1, "NFT wallet shouldn't exist yet"
|
|
@@@ -1010,13 -1010,13 +1009,13 @@@
|
|
|
1010
1009
|
await full_node_api.wait_for_wallet_synced(wallet_node_1, 20)
|
|
1011
1010
|
# transfer DID to the other wallet
|
|
1012
1011
|
txs = await did_wallet.transfer_did(ph1, uint64(0), True, DEFAULT_TX_CONFIG)
|
|
1012
|
++
txs = await did_wallet.wallet_state_manager.add_pending_transactions(txs)
|
|
1013
1013
|
for tx in txs:
|
|
1014
|
--
await did_wallet.wallet_state_manager.add_pending_transactions(txs)
|
|
1015
1014
|
if tx.spend_bundle is not None:
|
|
1016
1015
|
await time_out_assert_not_none(
|
|
1017
1016
|
30, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
|
|
1018
1017
|
)
|
|
1019
|
--
for
|
|
1018
|
++
for _ in range(1, num_blocks):
|
|
1020
1019
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
1021
1020
|
await full_node_api.wait_for_wallet_synced(wallet_node_0, 20)
|
|
1022
1021
|
await full_node_api.wait_for_wallet_synced(wallet_node_1, 20)
|
|
@@@ -1041,7 -1041,7 +1040,7 @@@
|
|
|
1041
1040
|
|
|
1042
1041
|
# wait for all wallets to be created
|
|
1043
1042
|
await time_out_assert(30, len, 3, wallet_1.wallet_state_manager.wallets)
|
|
1044
|
--
did_wallet_1 = wallet_1.wallet_state_manager.wallets[2]
|
|
1043
|
++
did_wallet_1 = wallet_1.wallet_state_manager.wallets[uint32(2)]
|
|
1045
1044
|
assert nft_wallet_0_id not in wallet_node_0.wallet_state_manager.wallets.keys()
|
|
1046
1045
|
# Check if the NFT owner DID is reset
|
|
1047
1046
|
resp = await api_1.nft_get_by_did({})
|
|
@@@ -1081,18 -1081,18 +1080,18 @@@
|
|
|
1081
1080
|
)
|
|
1082
1081
|
coins = resp["nft_list"]
|
|
1083
1082
|
assert len(coins) == 1
|
|
1083
|
++
assert coins[0].owner_did is not None
|
|
1084
1084
|
assert coins[0].owner_did.hex() == hex_did_id
|
|
1085
1085
|
|
|
1086
1086
|
|
|
1087
|
--
@pytest.mark.parametrize(
|
|
1088
|
--
"trusted",
|
|
1089
|
--
[True, False],
|
|
1090
|
--
)
|
|
1087
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
1091
1088
|
@pytest.mark.anyio
|
|
1092
|
--
async def test_update_metadata_for_nft_did(
|
|
1089
|
++
async def test_update_metadata_for_nft_did(
|
|
1090
|
++
self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool
|
|
1091
|
++
) -> None:
|
|
1093
1092
|
num_blocks = 3
|
|
1094
1093
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
1095
|
--
full_node_api
|
|
1094
|
++
full_node_api = full_nodes[0]
|
|
1096
1095
|
full_node_server = full_node_api.server
|
|
1097
1096
|
wallet_node_0, server_0 = wallets[0]
|
|
1098
1097
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -1125,12 -1125,12 +1124,11 @@@
|
|
|
1125
1124
|
|
|
1126
1125
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
1127
1126
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
1128
|
--
did_wallet
|
|
1129
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
1130
|
--
)
|
|
1127
|
++
did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
1131
1128
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
|
1132
1129
|
|
|
1133
1130
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
1131
|
++
assert spend_bundle is not None
|
|
1134
1132
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
1135
1133
|
|
|
1136
1134
|
for _ in range(1, num_blocks):
|
|
@@@ -1165,7 -1165,7 +1163,7 @@@
|
|
|
1165
1163
|
assert len(compute_memos(sb)) > 0
|
|
1166
1164
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
1167
1165
|
|
|
1168
|
--
for
|
|
1166
|
++
for _ in range(1, num_blocks):
|
|
1169
1167
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1170
1168
|
|
|
1171
1169
|
# Check DID NFT
|
|
@@@ -1173,8 -1173,8 +1171,9 @@@
|
|
|
1173
1171
|
coins_response = await wait_rpc_state_condition(
|
|
1174
1172
|
30, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
|
|
1175
1173
|
)
|
|
1176
|
--
coins = coins_response["nft_list"]
|
|
1174
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
1177
1175
|
assert len(coins) == 1
|
|
1176
|
++
assert coins[0].minter_did is not None
|
|
1178
1177
|
assert coins[0].minter_did.hex() == hex_did_id
|
|
1179
1178
|
nft_coin_id = coins[0].nft_coin_id
|
|
1180
1179
|
|
|
@@@ -1195,7 -1195,7 +1194,7 @@@
|
|
|
1195
1194
|
|
|
1196
1195
|
sb = tr1["spend_bundle"]
|
|
1197
1196
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
|
1198
|
--
for
|
|
1197
|
++
for _ in range(1, num_blocks):
|
|
1199
1198
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
|
1200
1199
|
# check that new URI was added
|
|
1201
1200
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, 11999999999898)
|
|
@@@ -1218,15 -1218,15 +1217,12 @@@
|
|
|
1218
1217
|
assert len(coin["license_uris"]) == 0
|
|
1219
1218
|
|
|
1220
1219
|
|
|
1221
|
--
@pytest.mark.parametrize(
|
|
1222
|
--
"trusted",
|
|
1223
|
--
[True, False],
|
|
1224
|
--
)
|
|
1220
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
1225
1221
|
@pytest.mark.anyio
|
|
1226
|
--
async def test_nft_bulk_set_did(self_hostname: str, two_wallet_nodes:
|
|
1222
|
++
async def test_nft_bulk_set_did(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
1227
1223
|
num_blocks = 2
|
|
1228
1224
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
1229
|
--
full_node_api
|
|
1225
|
++
full_node_api = full_nodes[0]
|
|
1230
1226
|
full_node_server = full_node_api.server
|
|
1231
1227
|
wallet_node_0, server_0 = wallets[0]
|
|
1232
1228
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -1257,11 -1257,11 +1253,10 @@@
|
|
|
1257
1253
|
|
|
1258
1254
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
1259
1255
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
1260
|
--
did_wallet
|
|
1261
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
1262
|
--
)
|
|
1256
|
++
did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
1263
1257
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
|
1264
1258
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
1259
|
++
assert spend_bundle is not None
|
|
1265
1260
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
1266
1261
|
|
|
1267
1262
|
for _ in range(1, num_blocks):
|
|
@@@ -1274,13 -1274,13 +1269,13 @@@
|
|
|
1274
1269
|
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=30)
|
|
1275
1270
|
|
|
1276
1271
|
res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id))
|
|
1277
|
--
assert isinstance(res, dict)
|
|
1278
1272
|
assert res.get("success")
|
|
1279
1273
|
nft_wallet_0_id = res["wallet_id"]
|
|
1274
|
++
assert isinstance(nft_wallet_0_id, uint32)
|
|
1280
1275
|
res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 2"))
|
|
1281
|
--
assert isinstance(res, dict)
|
|
1282
1276
|
assert res.get("success")
|
|
1283
1277
|
nft_wallet_1_id = res["wallet_id"]
|
|
1278
|
++
assert isinstance(nft_wallet_1_id, uint32)
|
|
1284
1279
|
await time_out_assert(30, did_wallet.get_confirmed_balance, 1)
|
|
1285
1280
|
|
|
1286
1281
|
# Create a NFT with DID
|
|
@@@ -1332,7 -1332,7 +1327,7 @@@
|
|
|
1332
1327
|
coins_response = await wait_rpc_state_condition(
|
|
1333
1328
|
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
|
|
1334
1329
|
)
|
|
1335
|
--
coins = coins_response["nft_list"]
|
|
1330
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
1336
1331
|
nft1 = coins[0]
|
|
1337
1332
|
nft12 = coins[1]
|
|
1338
1333
|
assert len(coins) == 2
|
|
@@@ -1353,8 -1353,8 +1348,12 @@@
|
|
|
1353
1348
|
{"nft_coin_id": nft2.nft_coin_id.hex()},
|
|
1354
1349
|
]
|
|
1355
1350
|
resp = await api_0.nft_set_did_bulk(dict(did_id=hmr_did_id, nft_coin_list=nft_coin_list, fee=1000))
|
|
1356
|
--
|
|
1357
|
--
assert
|
|
1351
|
++
sb = resp["spend_bundle"]
|
|
1352
|
++
assert isinstance(sb, SpendBundle)
|
|
1353
|
++
assert len(sb.coin_spends) == 5
|
|
1354
|
++
tx_num = resp["tx_num"]
|
|
1355
|
++
assert isinstance(tx_num, int)
|
|
1356
|
++
assert tx_num == 4
|
|
1358
1357
|
coins_response = await wait_rpc_state_condition(
|
|
1359
1358
|
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
|
|
1360
1359
|
)
|
|
@@@ -1373,23 -1373,23 +1372,25 @@@
|
|
|
1373
1372
|
[dict(wallet_id=nft_wallet_1_id)],
|
|
1374
1373
|
lambda x: len(x["nft_list"]) > 2 and x["nft_list"][0].owner_did,
|
|
1375
1374
|
)
|
|
1376
|
--
|
|
1375
|
++
nft_wallet_to_check = wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id]
|
|
1376
|
++
assert isinstance(nft_wallet_to_check, NFTWallet)
|
|
1377
|
++
assert await nft_wallet_to_check.get_nft_count() == 3
|
|
1377
1378
|
coins = resp["nft_list"]
|
|
1378
1379
|
assert len(coins) == 3
|
|
1380
|
++
assert coins[0].owner_did is not None
|
|
1379
1381
|
assert coins[0].owner_did.hex() == hex_did_id
|
|
1382
|
++
assert coins[1].owner_did is not None
|
|
1380
1383
|
assert coins[1].owner_did.hex() == hex_did_id
|
|
1384
|
++
assert coins[2].owner_did is not None
|
|
1381
1385
|
assert coins[2].owner_did.hex() == hex_did_id
|
|
1382
1386
|
|
|
1383
1387
|
|
|
1384
|
--
@pytest.mark.parametrize(
|
|
1385
|
--
"trusted",
|
|
1386
|
--
[True, False],
|
|
1387
|
--
)
|
|
1388
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
1388
1389
|
@pytest.mark.anyio
|
|
1389
|
--
async def test_nft_bulk_transfer(two_wallet_nodes:
|
|
1390
|
++
async def test_nft_bulk_transfer(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
1390
1391
|
num_blocks = 2
|
|
1391
1392
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
1392
|
--
full_node_api
|
|
1393
|
++
full_node_api = full_nodes[0]
|
|
1393
1394
|
full_node_server = full_node_api.server
|
|
1394
1395
|
wallet_node_0, server_0 = wallets[0]
|
|
1395
1396
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -1411,8 -1411,8 +1412,8 @@@
|
|
|
1411
1412
|
wallet_node_0.config["trusted_peers"] = {}
|
|
1412
1413
|
wallet_node_1.config["trusted_peers"] = {}
|
|
1413
1414
|
|
|
1414
|
--
await server_0.start_client(PeerInfo(
|
|
1415
|
--
await server_1.start_client(PeerInfo(
|
|
1415
|
++
await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
1416
|
++
await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
1416
1417
|
|
|
1417
1418
|
for _ in range(1, num_blocks + 1):
|
|
1418
1419
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
@@@ -1423,11 -1423,11 +1424,10 @@@
|
|
|
1423
1424
|
|
|
1424
1425
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
1425
1426
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
1426
|
--
did_wallet
|
|
1427
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
1428
|
--
)
|
|
1427
|
++
did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
1429
1428
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
|
1430
1429
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
1430
|
++
assert spend_bundle is not None
|
|
1431
1431
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
1432
1432
|
|
|
1433
1433
|
for _ in range(1, num_blocks):
|
|
@@@ -1496,7 -1496,7 +1496,7 @@@
|
|
|
1496
1496
|
coins_response = await wait_rpc_state_condition(
|
|
1497
1497
|
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) == 2
|
|
1498
1498
|
)
|
|
1499
|
--
coins = coins_response["nft_list"]
|
|
1499
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
1500
1500
|
nft1 = coins[0]
|
|
1501
1501
|
nft12 = coins[1]
|
|
1502
1502
|
assert len(coins) == 2
|
|
@@@ -1539,10 -1539,10 +1539,10 @@@
|
|
|
1539
1539
|
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
|
1540
1540
|
@pytest.mark.parametrize("trusted", [True, False])
|
|
1541
1541
|
@pytest.mark.anyio
|
|
1542
|
--
async def test_nft_set_did(self_hostname: str, two_wallet_nodes:
|
|
1542
|
++
async def test_nft_set_did(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
1543
1543
|
num_blocks = 3
|
|
1544
1544
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
1545
|
--
full_node_api
|
|
1545
|
++
full_node_api = full_nodes[0]
|
|
1546
1546
|
full_node_server = full_node_api.server
|
|
1547
1547
|
wallet_node_0, server_0 = wallets[0]
|
|
1548
1548
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -1573,11 -1573,11 +1573,10 @@@
|
|
|
1573
1573
|
|
|
1574
1574
|
await time_out_assert(30, wallet_0.get_unconfirmed_balance, funds)
|
|
1575
1575
|
await time_out_assert(30, wallet_0.get_confirmed_balance, funds)
|
|
1576
|
--
did_wallet
|
|
1577
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
1578
|
--
)
|
|
1576
|
++
did_wallet = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
1579
1577
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
|
1580
1578
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
1579
|
++
assert spend_bundle is not None
|
|
1581
1580
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
1582
1581
|
|
|
1583
1582
|
for _ in range(1, num_blocks):
|
|
@@@ -1613,18 -1613,18 +1612,17 @@@
|
|
|
1613
1612
|
coins_response = await wait_rpc_state_condition(
|
|
1614
1613
|
30, api_0.nft_get_nfts, [{"wallet_id": nft_wallet_0_id}], lambda x: len(x["nft_list"]) > 0
|
|
1615
1614
|
)
|
|
1616
|
--
coins = coins_response["nft_list"]
|
|
1615
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
1617
1616
|
assert len(coins) == 1
|
|
1618
1617
|
assert coins[0].owner_did is None
|
|
1619
1618
|
nft_coin_id = coins[0].nft_coin_id
|
|
1620
1619
|
|
|
1621
1620
|
# Test set None -> DID1
|
|
1622
|
--
did_wallet1
|
|
1623
|
--
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
|
1624
|
--
)
|
|
1621
|
++
did_wallet1 = await DIDWallet.create_new_did_wallet(wallet_node_0.wallet_state_manager, wallet_0, uint64(1))
|
|
1625
1622
|
spend_bundle_list = await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet1.id())
|
|
1626
1623
|
|
|
1627
1624
|
spend_bundle = spend_bundle_list[0].spend_bundle
|
|
1625
|
++
assert spend_bundle is not None
|
|
1628
1626
|
await time_out_assert_not_none(30, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
|
1629
1627
|
|
|
1630
1628
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
@@@ -1647,10 -1647,10 +1645,13 @@@
|
|
|
1647
1645
|
[dict(wallet_id=nft_wallet_1_id)],
|
|
1648
1646
|
lambda x: len(x["nft_list"]) > 0 and x["nft_list"][0].owner_did,
|
|
1649
1647
|
)
|
|
1650
|
--
|
|
1648
|
++
nft_wallet_to_check = wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id]
|
|
1649
|
++
assert isinstance(nft_wallet_to_check, NFTWallet)
|
|
1650
|
++
assert len(await nft_wallet_to_check.get_current_nfts()) == 0
|
|
1651
1651
|
|
|
1652
1652
|
coins = resp["nft_list"]
|
|
1653
1653
|
assert len(coins) == 1
|
|
1654
|
++
assert coins[0].owner_did is not None
|
|
1654
1655
|
assert coins[0].owner_did.hex() == hex_did_id
|
|
1655
1656
|
nft_coin_id = coins[0].nft_coin_id
|
|
1656
1657
|
|
|
@@@ -1682,6 -1682,6 +1683,7 @@@
|
|
|
1682
1683
|
assert resp.get("success")
|
|
1683
1684
|
coins = resp["nft_list"]
|
|
1684
1685
|
assert len(coins) == 1
|
|
1686
|
++
assert coins[0].owner_did is not None
|
|
1685
1687
|
assert coins[0].owner_did.hex() == hex_did_id
|
|
1686
1688
|
nft_coin_id = coins[0].nft_coin_id
|
|
1687
1689
|
resp = await api_0.nft_get_info(dict(coin_id=nft_coin_id.hex(), latest=True))
|
|
@@@ -1707,15 -1707,15 +1709,12 @@@
|
|
|
1707
1709
|
assert coins[0] == resp["nft_info"]
|
|
1708
1710
|
|
|
1709
1711
|
|
|
1710
|
--
@pytest.mark.parametrize(
|
|
1711
|
--
"trusted",
|
|
1712
|
--
[True, False],
|
|
1713
|
--
)
|
|
1712
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
1714
1713
|
@pytest.mark.anyio
|
|
1715
|
--
async def test_set_nft_status(self_hostname: str, two_wallet_nodes:
|
|
1714
|
++
async def test_set_nft_status(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
1716
1715
|
num_blocks = 5
|
|
1717
1716
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
1718
|
--
full_node_api
|
|
1717
|
++
full_node_api = full_nodes[0]
|
|
1719
1718
|
full_node_server = full_node_api.server
|
|
1720
1719
|
wallet_node_0, server_0 = wallets[0]
|
|
1721
1720
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -1776,7 -1776,7 +1775,7 @@@
|
|
|
1776
1775
|
)
|
|
1777
1776
|
assert coins_response["nft_list"], isinstance(coins_response, dict)
|
|
1778
1777
|
assert coins_response.get("success")
|
|
1779
|
--
coins = coins_response["nft_list"]
|
|
1778
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
1780
1779
|
assert len(coins) == 1
|
|
1781
1780
|
assert coins[0].owner_did is None
|
|
1782
1781
|
assert not coins[0].pending_transaction
|
|
@@@ -1793,15 -1793,15 +1792,12 @@@
|
|
|
1793
1792
|
assert coins[0].pending_transaction
|
|
1794
1793
|
|
|
1795
1794
|
|
|
1796
|
--
@pytest.mark.parametrize(
|
|
1797
|
--
"trusted",
|
|
1798
|
--
[True, False],
|
|
1799
|
--
)
|
|
1795
|
++
@pytest.mark.parametrize("trusted", [True, False])
|
|
1800
1796
|
@pytest.mark.anyio
|
|
1801
|
--
async def test_nft_sign_message(self_hostname: str, two_wallet_nodes:
|
|
1797
|
++
async def test_nft_sign_message(self_hostname: str, two_wallet_nodes: OldSimulatorsAndWallets, trusted: bool) -> None:
|
|
1802
1798
|
num_blocks = 5
|
|
1803
1799
|
full_nodes, wallets, _ = two_wallet_nodes
|
|
1804
|
--
full_node_api
|
|
1800
|
++
full_node_api = full_nodes[0]
|
|
1805
1801
|
full_node_server = full_node_api.server
|
|
1806
1802
|
wallet_node_0, server_0 = wallets[0]
|
|
1807
1803
|
wallet_node_1, server_1 = wallets[1]
|
|
@@@ -1862,7 -1862,7 +1858,7 @@@
|
|
|
1862
1858
|
)
|
|
1863
1859
|
assert coins_response["nft_list"], isinstance(coins_response, dict)
|
|
1864
1860
|
assert coins_response.get("success")
|
|
1865
|
--
coins = coins_response["nft_list"]
|
|
1861
|
++
coins: List[NFTInfo] = coins_response["nft_list"]
|
|
1866
1862
|
assert len(coins) == 1
|
|
1867
1863
|
assert coins[0].owner_did is None
|
|
1868
1864
|
assert not coins[0].pending_transaction
|
|
@@@ -1871,7 -1871,7 +1867,7 @@@
|
|
|
1871
1867
|
response = await api_0.sign_message_by_id(
|
|
1872
1868
|
{"id": encode_puzzle_hash(coins[0].launcher_id, AddressType.NFT.value), "message": message}
|
|
1873
1869
|
)
|
|
1874
|
--
puzzle
|
|
1870
|
++
puzzle = Program.to((CHIP_0002_SIGN_MESSAGE_PREFIX, message))
|
|
1875
1871
|
assert AugSchemeMPL.verify(
|
|
1876
1872
|
G1Element.from_bytes(bytes.fromhex(response["pubkey"])),
|
|
1877
1873
|
puzzle.get_tree_hash(),
|
|
@@@ -536,20 -536,20 +536,36 @@@ async def test_create_signed_transactio
|
|
|
536
536
|
await farm_transaction(full_node_api, wallet_1_node, spend_bundle)
|
|
537
537
|
await time_out_assert(20, get_confirmed_balance, generated_funds - amount_total, wallet_1_rpc, wallet_id)
|
|
538
538
|
|
|
539
|
--
#
|
|
539
|
++
# Assert every coin comes from the same parent
|
|
540
|
++
additions: List[Coin] = spend_bundle.additions()
|
|
541
|
++
assert len({c.parent_coin_info for c in additions}) == 2 if is_cat else 1
|
|
542
|
++
|
|
543
|
++
# Assert you can get the spend for each addition
|
|
544
|
++
for addition in additions:
|
|
545
|
++
cr: Optional[CoinRecord] = await full_node_rpc.get_coin_record_by_name(addition.name())
|
|
546
|
++
assert cr is not None
|
|
547
|
++
spend: Optional[CoinSpend] = await full_node_rpc.get_puzzle_and_solution(
|
|
548
|
++
addition.parent_coin_info, cr.confirmed_block_index
|
|
549
|
++
)
|
|
550
|
++
assert spend is not None
|
|
551
|
++
|
|
552
|
++
# Assert the memos are all correct
|
|
553
|
++
addition_dict: Dict[bytes32, Coin] = {addition.name(): addition for addition in additions}
|
|
554
|
++
memo_dictionary: Dict[bytes32, List[bytes]] = compute_memos(spend_bundle)
|
|
540
555
|
for output in outputs:
|
|
541
|
--
if "memos" in
|
|
556
|
++
if "memos" in output:
|
|
542
557
|
found: bool = False
|
|
543
|
--
for addition in
|
|
544
|
--
if
|
|
545
|
--
|
|
546
|
--
|
|
547
|
--
|
|
548
|
--
|
|
549
|
--
|
|
550
|
--
|
|
551
|
--
|
|
552
|
--
|
|
558
|
++
for addition_id, addition in addition_dict.items():
|
|
559
|
++
if (
|
|
560
|
++
is_cat
|
|
561
|
++
and addition.amount == output["amount"]
|
|
562
|
++
and memo_dictionary[addition_id][0] == output["puzzle_hash"]
|
|
563
|
++
and memo_dictionary[addition_id][1:] == [memo.encode() for memo in output["memos"]]
|
|
564
|
++
) or (
|
|
565
|
++
addition.amount == output["amount"]
|
|
566
|
++
and addition.puzzle_hash == output["puzzle_hash"]
|
|
567
|
++
and memo_dictionary[addition_id] == [memo.encode() for memo in output["memos"]]
|
|
568
|
++
):
|
|
553
569
|
found = True
|
|
554
570
|
assert found
|
|
555
571
|
|
|
@@@ -913,13 -913,13 +929,12 @@@ async def test_get_transaction_count(wa
|
|
|
913
929
|
assert len(all_transactions) > 0
|
|
914
930
|
transaction_count = await client.get_transaction_count(1)
|
|
915
931
|
assert transaction_count == len(all_transactions)
|
|
916
|
--
|
|
917
|
--
assert
|
|
918
|
--
|
|
919
|
--
|
|
920
|
--
)
|
|
921
|
--
== 0
|
|
932
|
++
transaction_count = await client.get_transaction_count(1, confirmed=False)
|
|
933
|
++
assert transaction_count == 0
|
|
934
|
++
transaction_count = await client.get_transaction_count(
|
|
935
|
++
1, type_filter=TransactionTypeFilter.include([TransactionType.INCOMING_CLAWBACK_SEND])
|
|
922
936
|
)
|
|
937
|
++
assert transaction_count == 0
|
|
923
938
|
|
|
924
939
|
|
|
925
940
|
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
|
@@@ -1825,12 -1825,12 +1840,12 @@@ async def test_select_coins_rpc(wallet_
|
|
|
1825
1840
|
excluded_coin_ids=[c.name() for c in excluded_amt_coins]
|
|
1826
1841
|
),
|
|
1827
1842
|
)
|
|
1828
|
--
assert excluded_amt_coins
|
|
1843
|
++
assert set(excluded_amt_coins).intersection({rec.coin for rec in all_coins}) == set()
|
|
1829
1844
|
all_coins, _, _ = await client_2.get_spendable_coins(
|
|
1830
1845
|
wallet_id=1,
|
|
1831
1846
|
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(excluded_coin_amounts=[uint64(1000)]),
|
|
1832
1847
|
)
|
|
1833
|
--
assert
|
|
1848
|
++
assert len([rec for rec in all_coins if rec.coin.amount == 1000]) == 0
|
|
1834
1849
|
all_coins_2, _, _ = await client_2.get_spendable_coins(
|
|
1835
1850
|
wallet_id=1,
|
|
1836
1851
|
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG.override(max_coin_amount=uint64(999)),
|
|
@@@ -17,6 -17,6 +17,7 @@@ from chia.server.outbound_message impor
|
|
|
17
17
|
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol
|
|
18
18
|
from chia.simulator.wallet_tools import WalletTool
|
|
19
19
|
from chia.types.blockchain_format.coin import Coin
|
|
20
|
++
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
20
21
|
from chia.types.coin_record import CoinRecord
|
|
21
22
|
from chia.types.condition_opcodes import ConditionOpcode
|
|
22
23
|
from chia.types.condition_with_args import ConditionWithArgs
|
|
@@@ -30,16 -30,16 +31,10 @@@ from tests.connection_utils import add_
|
|
|
30
31
|
from tests.util.setup_nodes import OldSimulatorsAndWallets
|
|
31
32
|
from tests.util.time_out_assert import time_out_assert
|
|
32
33
|
|
|
33
|
--
|
|
34
|
--
def wallet_height_at_least(wallet_node, h):
|
|
35
|
--
height = wallet_node.wallet_state_manager.blockchain._peak_height
|
|
36
|
--
if height == h:
|
|
37
|
--
return True
|
|
38
|
--
return False
|
|
39
|
--
|
|
40
|
--
|
|
41
34
|
log = getLogger(__name__)
|
|
42
35
|
|
|
36
|
++
zero_ph = bytes32(32 * b"\0")
|
|
37
|
++
|
|
43
38
|
|
|
44
39
|
async def get_all_messages_in_queue(queue):
|
|
45
40
|
all_messages = []
|
|
@@@ -50,483 -50,483 +45,605 @@@
|
|
|
50
45
|
return all_messages
|
|
51
46
|
|
|
52
47
|
|
|
53
|
--
|
|
54
|
--
|
|
55
|
--
|
|
56
|
--
|
|
57
|
--
|
|
58
|
--
|
|
59
|
--
|
|
60
|
--
|
|
61
|
--
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
62
|
--
|
|
63
|
--
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
64
|
--
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
65
|
--
|
|
66
|
--
zero_ph = 32 * b"\0"
|
|
67
|
--
junk_ph = 32 * b"\a"
|
|
68
|
--
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
69
|
--
msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
|
|
70
|
--
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
71
|
--
|
|
72
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
73
|
--
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
74
|
--
assert data_response.coin_states == []
|
|
75
|
--
|
|
76
|
--
# Farm few more with reward
|
|
77
|
--
for i in range(0, num_blocks):
|
|
78
|
--
if i == num_blocks - 1:
|
|
79
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
80
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
81
|
--
else:
|
|
82
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
83
|
--
|
|
84
|
--
msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
|
|
85
|
--
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
86
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
87
|
--
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
88
|
--
# we have already subscribed to this puzzle hash, it will be ignored
|
|
89
|
--
# we still receive the updates (see below)
|
|
90
|
--
assert data_response.coin_states == []
|
|
91
|
--
|
|
92
|
--
# Farm more rewards to check the incoming queue for the updates
|
|
93
|
--
for i in range(0, num_blocks):
|
|
94
|
--
if i == num_blocks - 1:
|
|
95
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
96
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
97
|
--
else:
|
|
98
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
99
|
--
|
|
100
|
--
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
101
|
--
|
|
102
|
--
zero_coin = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {zero_ph})
|
|
103
|
--
all_zero_coin = set(zero_coin)
|
|
104
|
--
notified_zero_coins = set()
|
|
105
|
--
|
|
106
|
--
for message in all_messages:
|
|
107
|
--
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
108
|
--
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
109
|
--
for coin_state in data_response.items:
|
|
110
|
--
notified_zero_coins.add(coin_state)
|
|
111
|
--
assert len(data_response.items) == 2 # 2 per height farmer / pool reward
|
|
112
|
--
|
|
113
|
--
assert all_zero_coin == notified_zero_coins
|
|
114
|
--
|
|
115
|
--
# Test subscribing to more coins
|
|
116
|
--
one_ph = 32 * b"\1"
|
|
117
|
--
msg = wallet_protocol.RegisterForPhUpdates([one_ph], 0)
|
|
118
|
--
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
119
|
--
peak = full_node_api.full_node.blockchain.get_peak()
|
|
120
|
--
|
|
121
|
--
for i in range(0, num_blocks):
|
|
122
|
--
if i == num_blocks - 1:
|
|
123
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
124
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
125
|
--
else:
|
|
126
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
127
|
--
|
|
128
|
--
for i in range(0, num_blocks):
|
|
129
|
--
if i == num_blocks - 1:
|
|
130
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
|
|
131
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
132
|
--
else:
|
|
133
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
|
|
134
|
--
|
|
135
|
--
zero_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(
|
|
136
|
--
True, {zero_ph}, peak.height + 1
|
|
137
|
--
)
|
|
138
|
--
one_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {one_ph})
|
|
48
|
++
@pytest.mark.anyio
|
|
49
|
++
async def test_subscribe_for_ph(simulator_and_wallet, self_hostname):
|
|
50
|
++
num_blocks = 4
|
|
51
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
52
|
++
full_node_api = full_nodes[0]
|
|
53
|
++
wallet_node, server_2 = wallets[0]
|
|
54
|
++
fn_server = full_node_api.full_node.server
|
|
55
|
++
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
139
56
|
|
|
140
|
--
|
|
141
|
--
|
|
57
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
58
|
++
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
142
59
|
|
|
143
|
--
|
|
60
|
++
junk_ph = 32 * b"\a"
|
|
61
|
++
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
62
|
++
msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
|
|
63
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
144
64
|
|
|
145
|
--
|
|
146
|
--
|
|
147
|
--
|
|
148
|
--
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
149
|
--
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
150
|
--
for coin_state in data_response.items:
|
|
151
|
--
notified_all_coins.add(coin_state)
|
|
152
|
--
assert len(data_response.items) == 2 # 2 per height farmer / pool reward
|
|
65
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
66
|
++
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
67
|
++
assert data_response.coin_states == []
|
|
153
68
|
|
|
154
|
--
|
|
155
|
--
|
|
156
|
--
|
|
157
|
--
|
|
158
|
--
|
|
159
|
--
|
|
160
|
--
|
|
161
|
--
if i == num_blocks - 1:
|
|
162
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
|
|
163
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
164
|
--
else:
|
|
165
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
|
|
166
|
--
|
|
167
|
--
funds = sum(
|
|
168
|
--
[
|
|
169
|
--
calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
|
|
170
|
--
for i in range(1, num_blocks + 1)
|
|
171
|
--
]
|
|
172
|
--
)
|
|
173
|
--
fn_amount = sum(
|
|
174
|
--
cr.coin.amount
|
|
175
|
--
for cr in await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, puzzle_hash)
|
|
176
|
--
)
|
|
177
|
--
|
|
178
|
--
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
179
|
--
assert funds == fn_amount
|
|
69
|
++
# Farm few more with reward
|
|
70
|
++
for i in range(0, num_blocks):
|
|
71
|
++
if i == num_blocks - 1:
|
|
72
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
73
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
74
|
++
else:
|
|
75
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
180
76
|
|
|
181
|
--
|
|
182
|
--
|
|
183
|
--
|
|
184
|
--
|
|
185
|
--
|
|
77
|
++
msg = wallet_protocol.RegisterForPhUpdates([zero_ph], 0)
|
|
78
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
79
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
80
|
++
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
81
|
++
# we have already subscribed to this puzzle hash, it will be ignored
|
|
82
|
++
# we still receive the updates (see below)
|
|
83
|
++
assert data_response.coin_states == []
|
|
84
|
++
|
|
85
|
++
# Farm more rewards to check the incoming queue for the updates
|
|
86
|
++
for i in range(0, num_blocks):
|
|
87
|
++
if i == num_blocks - 1:
|
|
88
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
89
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
90
|
++
else:
|
|
91
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
186
92
|
|
|
187
|
--
|
|
93
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
188
94
|
|
|
189
|
--
|
|
190
|
--
|
|
191
|
--
|
|
192
|
--
assert spent_coin.puzzle_hash == puzzle_hash
|
|
95
|
++
zero_coin = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {zero_ph})
|
|
96
|
++
all_zero_coin = set(zero_coin)
|
|
97
|
++
notified_zero_coins = set()
|
|
193
98
|
|
|
194
|
--
|
|
99
|
++
for message in all_messages:
|
|
100
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
101
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
102
|
++
for coin_state in data_response.items:
|
|
103
|
++
notified_zero_coins.add(coin_state)
|
|
104
|
++
assert len(data_response.items) == 2 # 2 per height farmer / pool reward
|
|
195
105
|
|
|
196
|
--
|
|
106
|
++
assert all_zero_coin == notified_zero_coins
|
|
197
107
|
|
|
198
|
--
|
|
199
|
--
|
|
108
|
++
# Test subscribing to more coins
|
|
109
|
++
one_ph = 32 * b"\1"
|
|
110
|
++
msg = wallet_protocol.RegisterForPhUpdates([one_ph], 0)
|
|
111
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
112
|
++
peak = full_node_api.full_node.blockchain.get_peak()
|
|
200
113
|
|
|
201
|
--
|
|
114
|
++
for i in range(0, num_blocks):
|
|
115
|
++
if i == num_blocks - 1:
|
|
116
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
117
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
118
|
++
else:
|
|
119
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
202
120
|
|
|
203
|
--
|
|
204
|
--
|
|
205
|
--
|
|
206
|
--
|
|
121
|
++
for i in range(0, num_blocks):
|
|
122
|
++
if i == num_blocks - 1:
|
|
123
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
|
|
124
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
125
|
++
else:
|
|
126
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(one_ph))
|
|
207
127
|
|
|
208
|
--
|
|
128
|
++
zero_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(
|
|
129
|
++
True, {zero_ph}, peak.height + 1
|
|
130
|
++
)
|
|
131
|
++
one_coins = await full_node_api.full_node.coin_store.get_coin_states_by_puzzle_hashes(True, {one_ph})
|
|
209
132
|
|
|
210
|
--
|
|
133
|
++
all_coins = set(zero_coins)
|
|
134
|
++
all_coins.update(one_coins)
|
|
211
135
|
|
|
212
|
--
|
|
213
|
--
[tx_record] = await wallet.generate_signed_transaction(uint64(10), junk_ph, DEFAULT_TX_CONFIG, uint64(0))
|
|
214
|
--
await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
|
|
136
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
215
137
|
|
|
216
|
--
|
|
138
|
++
notified_all_coins = set()
|
|
217
139
|
|
|
218
|
--
|
|
140
|
++
for message in all_messages:
|
|
141
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
142
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
143
|
++
for coin_state in data_response.items:
|
|
144
|
++
notified_all_coins.add(coin_state)
|
|
145
|
++
assert len(data_response.items) == 2 # 2 per height farmer / pool reward
|
|
219
146
|
|
|
220
|
--
|
|
147
|
++
assert all_coins == notified_all_coins
|
|
221
148
|
|
|
222
|
--
|
|
223
|
--
|
|
224
|
--
|
|
225
|
--
for coin_state in data_response.items:
|
|
226
|
--
if coin_state.coin.name() == spent_coin.name():
|
|
227
|
--
notified_state = coin_state
|
|
149
|
++
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
150
|
++
wallet: Wallet = wsm.wallets[1]
|
|
151
|
++
puzzle_hash = await wallet.get_new_puzzlehash()
|
|
228
152
|
|
|
229
|
--
|
|
230
|
--
|
|
231
|
--
|
|
232
|
--
|
|
233
|
--
|
|
234
|
--
async def test_subscribe_for_coin_id(self, simulator_and_wallet, self_hostname):
|
|
235
|
--
num_blocks = 4
|
|
236
|
--
full_nodes, wallets, _ = simulator_and_wallet
|
|
237
|
--
full_node_api = full_nodes[0]
|
|
238
|
--
wallet_node, server_2 = wallets[0]
|
|
239
|
--
fn_server = full_node_api.full_node.server
|
|
240
|
--
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
241
|
--
standard_wallet: Wallet = wsm.wallets[1]
|
|
242
|
--
puzzle_hash = await standard_wallet.get_new_puzzlehash()
|
|
243
|
--
|
|
244
|
--
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
245
|
--
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
246
|
--
|
|
247
|
--
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
248
|
--
|
|
249
|
--
# Farm to create a coin that we'll track
|
|
250
|
--
for i in range(0, num_blocks):
|
|
153
|
++
for i in range(0, num_blocks):
|
|
154
|
++
if i == num_blocks - 1:
|
|
155
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
|
|
156
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(junk_ph))
|
|
157
|
++
else:
|
|
251
158
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
|
|
252
159
|
|
|
253
|
--
|
|
254
|
--
|
|
255
|
--
|
|
160
|
++
funds = sum(
|
|
161
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks + 1)]
|
|
162
|
++
)
|
|
163
|
++
fn_amount = sum(
|
|
164
|
++
cr.coin.amount
|
|
165
|
++
for cr in await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(False, puzzle_hash)
|
|
166
|
++
)
|
|
256
167
|
|
|
257
|
--
|
|
168
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
169
|
++
assert funds == fn_amount
|
|
258
170
|
|
|
259
|
--
|
|
260
|
--
|
|
261
|
--
|
|
262
|
--
|
|
171
|
++
msg_1 = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0)
|
|
172
|
++
msg_response_1 = await full_node_api.register_interest_in_puzzle_hash(msg_1, fake_wallet_peer)
|
|
173
|
++
assert msg_response_1.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
174
|
++
data_response_1: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response_1.data)
|
|
175
|
++
assert len(data_response_1.coin_states) == 2 * num_blocks # 2 per height farmer / pool reward
|
|
263
176
|
|
|
264
|
--
|
|
265
|
--
msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
|
|
266
|
--
assert msg_response is not None
|
|
267
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
268
|
--
data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
269
|
--
assert data_response.coin_states[0].coin == coin_to_spend
|
|
177
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
270
178
|
|
|
271
|
--
|
|
272
|
--
|
|
273
|
--
|
|
274
|
--
|
|
275
|
--
)
|
|
276
|
--
await standard_wallet.wallet_state_manager.add_pending_transactions([tx_record])
|
|
179
|
++
[tx_record] = await wallet.generate_signed_transaction(uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0))
|
|
180
|
++
assert len(tx_record.spend_bundle.removals()) == 1
|
|
181
|
++
spent_coin = tx_record.spend_bundle.removals()[0]
|
|
182
|
++
assert spent_coin.puzzle_hash == puzzle_hash
|
|
277
183
|
|
|
278
|
--
|
|
184
|
++
await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
|
|
279
185
|
|
|
280
|
--
|
|
186
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
281
187
|
|
|
282
|
--
|
|
283
|
--
|
|
284
|
--
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
285
|
--
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
286
|
--
for coin_state in data_response.items:
|
|
287
|
--
notified_coins.add(coin_state.coin)
|
|
288
|
--
assert coin_state.spent_height is not None
|
|
188
|
++
# Let's make sure the wallet can handle a non ephemeral launcher
|
|
189
|
++
from chia.wallet.puzzles.singleton_top_layer import SINGLETON_LAUNCHER_HASH
|
|
289
190
|
|
|
290
|
--
|
|
191
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
291
192
|
|
|
292
|
--
|
|
293
|
--
|
|
193
|
++
[tx_record] = await wallet.generate_signed_transaction(
|
|
194
|
++
uint64(10), SINGLETON_LAUNCHER_HASH, DEFAULT_TX_CONFIG, uint64(0)
|
|
195
|
++
)
|
|
196
|
++
await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
|
|
294
197
|
|
|
295
|
--
|
|
296
|
--
uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0)
|
|
297
|
--
)
|
|
198
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
298
199
|
|
|
299
|
--
|
|
200
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
300
201
|
|
|
301
|
--
|
|
302
|
--
|
|
303
|
--
|
|
304
|
--
added_target = coin
|
|
202
|
++
# Send a transaction to make sure the wallet is still running
|
|
203
|
++
[tx_record] = await wallet.generate_signed_transaction(uint64(10), junk_ph, DEFAULT_TX_CONFIG, uint64(0))
|
|
204
|
++
await wallet_node.wallet_state_manager.add_pending_transactions([tx_record])
|
|
305
205
|
|
|
306
|
--
|
|
206
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
307
207
|
|
|
308
|
--
|
|
309
|
--
msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
|
|
310
|
--
assert msg_response is not None
|
|
311
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
312
|
--
data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
313
|
--
assert len(data_response.coin_states) == 0
|
|
208
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
314
209
|
|
|
315
|
--
|
|
210
|
++
notified_state = None
|
|
316
211
|
|
|
317
|
--
|
|
212
|
++
for message in all_messages:
|
|
213
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
214
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
215
|
++
for coin_state in data_response.items:
|
|
216
|
++
if coin_state.coin.name() == spent_coin.name():
|
|
217
|
++
notified_state = coin_state
|
|
318
218
|
|
|
319
|
--
|
|
219
|
++
assert notified_state is not None
|
|
220
|
++
assert notified_state.coin == spent_coin
|
|
221
|
++
assert notified_state.spent_height is not None
|
|
320
222
|
|
|
321
|
--
notified_state = None
|
|
322
223
|
|
|
323
|
--
|
|
324
|
--
|
|
325
|
--
|
|
326
|
--
|
|
327
|
--
|
|
328
|
--
|
|
224
|
++
@pytest.mark.anyio
|
|
225
|
++
async def test_subscribe_for_coin_id(simulator_and_wallet, self_hostname):
|
|
226
|
++
num_blocks = 4
|
|
227
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
228
|
++
full_node_api = full_nodes[0]
|
|
229
|
++
wallet_node, server_2 = wallets[0]
|
|
230
|
++
fn_server = full_node_api.full_node.server
|
|
231
|
++
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
232
|
++
standard_wallet: Wallet = wsm.wallets[1]
|
|
233
|
++
puzzle_hash = await standard_wallet.get_new_puzzlehash()
|
|
329
234
|
|
|
330
|
--
|
|
331
|
--
|
|
332
|
--
assert notified_state.spent_height is None
|
|
333
|
--
|
|
334
|
--
@pytest.mark.anyio
|
|
335
|
--
async def test_subscribe_for_ph_reorg(self, simulator_and_wallet, self_hostname):
|
|
336
|
--
num_blocks = 4
|
|
337
|
--
long_blocks = 20
|
|
338
|
--
full_nodes, wallets, _ = simulator_and_wallet
|
|
339
|
--
full_node_api = full_nodes[0]
|
|
340
|
--
wallet_node, server_2 = wallets[0]
|
|
341
|
--
fn_server = full_node_api.full_node.server
|
|
342
|
--
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
343
|
--
standard_wallet: Wallet = wsm.wallets[1]
|
|
344
|
--
puzzle_hash = await standard_wallet.get_new_puzzlehash()
|
|
345
|
--
|
|
346
|
--
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
347
|
--
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
348
|
--
|
|
349
|
--
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
350
|
--
zero_ph = 32 * b"\0"
|
|
351
|
--
|
|
352
|
--
# Farm to create a coin that we'll track
|
|
353
|
--
for i in range(0, num_blocks):
|
|
354
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
235
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
236
|
++
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
355
237
|
|
|
356
|
--
|
|
357
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
238
|
++
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
358
239
|
|
|
359
|
--
|
|
360
|
--
|
|
361
|
--
assert msg_response is not None
|
|
240
|
++
# Farm to create a coin that we'll track
|
|
241
|
++
for i in range(0, num_blocks):
|
|
362
242
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
|
|
363
243
|
|
|
364
|
--
|
|
365
|
--
|
|
244
|
++
funds = sum(
|
|
245
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
|
|
246
|
++
)
|
|
366
247
|
|
|
367
|
--
|
|
368
|
--
await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, expected_height)
|
|
248
|
++
await time_out_assert(20, standard_wallet.get_confirmed_balance, funds)
|
|
369
249
|
|
|
370
|
--
|
|
371
|
--
|
|
372
|
--
|
|
373
|
--
|
|
374
|
--
await full_node_api.reorg_from_index_to_new_index(req)
|
|
250
|
++
my_coins: List[CoinRecord] = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(
|
|
251
|
++
True, puzzle_hash
|
|
252
|
++
)
|
|
253
|
++
coin_to_spend = my_coins[0].coin
|
|
375
254
|
|
|
376
|
--
|
|
377
|
--
|
|
255
|
++
msg = wallet_protocol.RegisterForCoinUpdates([coin_to_spend.name()], 0)
|
|
256
|
++
msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
|
|
257
|
++
assert msg_response is not None
|
|
258
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
259
|
++
data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
260
|
++
assert data_response.coin_states[0].coin == coin_to_spend
|
|
378
261
|
|
|
379
|
--
|
|
262
|
++
coins = set()
|
|
263
|
++
coins.add(coin_to_spend)
|
|
264
|
++
[tx_record] = await standard_wallet.generate_signed_transaction(
|
|
265
|
++
uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0), coins=coins
|
|
266
|
++
)
|
|
267
|
++
await standard_wallet.wallet_state_manager.add_pending_transactions([tx_record])
|
|
380
268
|
|
|
381
|
--
|
|
382
|
--
for message in all_messages:
|
|
383
|
--
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
384
|
--
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
385
|
--
coin_update_messages.append(data_response)
|
|
386
|
--
|
|
387
|
--
# First state is creation, second one is a reorg
|
|
388
|
--
assert len(coin_update_messages) == 2
|
|
389
|
--
first = coin_update_messages[0]
|
|
390
|
--
|
|
391
|
--
assert len(first.items) == 2
|
|
392
|
--
first_state_coin_1 = first.items[0]
|
|
393
|
--
assert first_state_coin_1.spent_height is None
|
|
394
|
--
assert first_state_coin_1.created_height is not None
|
|
395
|
--
first_state_coin_2 = first.items[1]
|
|
396
|
--
assert first_state_coin_2.spent_height is None
|
|
397
|
--
assert first_state_coin_2.created_height is not None
|
|
398
|
--
|
|
399
|
--
second = coin_update_messages[1]
|
|
400
|
--
assert second.fork_height == fork_height
|
|
401
|
--
assert len(second.items) == 2
|
|
402
|
--
second_state_coin_1 = second.items[0]
|
|
403
|
--
assert second_state_coin_1.spent_height is None
|
|
404
|
--
assert second_state_coin_1.created_height is None
|
|
405
|
--
second_state_coin_2 = second.items[1]
|
|
406
|
--
assert second_state_coin_2.spent_height is None
|
|
407
|
--
assert second_state_coin_2.created_height is None
|
|
408
|
--
|
|
409
|
--
@pytest.mark.anyio
|
|
410
|
--
async def test_subscribe_for_coin_id_reorg(self, simulator_and_wallet, self_hostname):
|
|
411
|
--
num_blocks = 4
|
|
412
|
--
long_blocks = 20
|
|
413
|
--
full_nodes, wallets, _ = simulator_and_wallet
|
|
414
|
--
full_node_api = full_nodes[0]
|
|
415
|
--
wallet_node, server_2 = wallets[0]
|
|
416
|
--
fn_server = full_node_api.full_node.server
|
|
417
|
--
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
418
|
--
standard_wallet: Wallet = wsm.wallets[1]
|
|
419
|
--
puzzle_hash = await standard_wallet.get_new_puzzlehash()
|
|
420
|
--
|
|
421
|
--
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
422
|
--
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
423
|
--
|
|
424
|
--
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
425
|
--
zero_ph = 32 * b"\0"
|
|
426
|
--
|
|
427
|
--
# Farm to create a coin that we'll track
|
|
428
|
--
for i in range(0, num_blocks):
|
|
429
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
269
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
430
270
|
|
|
431
|
--
|
|
432
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
271
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
433
272
|
|
|
434
|
--
|
|
273
|
++
notified_coins = set()
|
|
274
|
++
for message in all_messages:
|
|
275
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
276
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
277
|
++
for coin_state in data_response.items:
|
|
278
|
++
notified_coins.add(coin_state.coin)
|
|
279
|
++
assert coin_state.spent_height is not None
|
|
435
280
|
|
|
436
|
--
|
|
437
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
281
|
++
assert notified_coins == coins
|
|
438
282
|
|
|
439
|
--
|
|
440
|
--
|
|
283
|
++
# Test getting notification for coin that is about to be created
|
|
284
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
441
285
|
|
|
442
|
--
|
|
443
|
--
|
|
286
|
++
[tx_record] = await standard_wallet.generate_signed_transaction(
|
|
287
|
++
uint64(10), puzzle_hash, DEFAULT_TX_CONFIG, uint64(0)
|
|
288
|
++
)
|
|
444
289
|
|
|
445
|
--
|
|
446
|
--
|
|
447
|
--
|
|
448
|
--
|
|
290
|
++
added_target: Optional[Coin] = None
|
|
291
|
++
for coin in tx_record.spend_bundle.additions():
|
|
292
|
++
if coin.puzzle_hash == puzzle_hash:
|
|
293
|
++
added_target = coin
|
|
294
|
++
|
|
295
|
++
assert added_target is not None
|
|
296
|
++
|
|
297
|
++
msg = wallet_protocol.RegisterForCoinUpdates([added_target.name()], 0)
|
|
298
|
++
msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
|
|
299
|
++
assert msg_response is not None
|
|
300
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
301
|
++
data_response: RespondToCoinUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
302
|
++
assert len(data_response.coin_states) == 0
|
|
303
|
++
|
|
304
|
++
await standard_wallet.wallet_state_manager.add_pending_transactions([tx_record])
|
|
305
|
++
|
|
306
|
++
await full_node_api.process_transaction_records(records=[tx_record])
|
|
307
|
++
|
|
308
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
309
|
++
|
|
310
|
++
notified_state = None
|
|
311
|
++
|
|
312
|
++
for message in all_messages:
|
|
313
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
314
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
315
|
++
for coin_state in data_response.items:
|
|
316
|
++
if coin_state.coin.name() == added_target.name():
|
|
317
|
++
notified_state = coin_state
|
|
318
|
++
|
|
319
|
++
assert notified_state is not None
|
|
320
|
++
assert notified_state.coin == added_target
|
|
321
|
++
assert notified_state.spent_height is None
|
|
322
|
++
|
|
323
|
++
|
|
324
|
++
@pytest.mark.anyio
|
|
325
|
++
async def test_subscribe_for_ph_reorg(simulator_and_wallet, self_hostname):
|
|
326
|
++
num_blocks = 4
|
|
327
|
++
long_blocks = 20
|
|
328
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
329
|
++
full_node_api = full_nodes[0]
|
|
330
|
++
wallet_node, server_2 = wallets[0]
|
|
331
|
++
fn_server = full_node_api.full_node.server
|
|
332
|
++
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
333
|
++
standard_wallet: Wallet = wsm.wallets[1]
|
|
334
|
++
puzzle_hash = await standard_wallet.get_new_puzzlehash()
|
|
335
|
++
|
|
336
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
337
|
++
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
338
|
++
|
|
339
|
++
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
340
|
++
|
|
341
|
++
# Farm to create a coin that we'll track
|
|
342
|
++
for i in range(0, num_blocks):
|
|
343
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
344
|
++
|
|
345
|
++
for i in range(0, long_blocks):
|
|
346
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
347
|
++
|
|
348
|
++
msg = wallet_protocol.RegisterForPhUpdates([puzzle_hash], 0)
|
|
349
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
350
|
++
assert msg_response is not None
|
|
351
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
|
|
352
|
++
|
|
353
|
++
for i in range(0, num_blocks):
|
|
354
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
355
|
++
|
|
356
|
++
expected_height = uint32(long_blocks + 2 * num_blocks + 1)
|
|
357
|
++
await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, expected_height)
|
|
358
|
++
|
|
359
|
++
coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
|
|
360
|
++
assert len(coin_records) > 0
|
|
361
|
++
fork_height = expected_height - num_blocks - 5
|
|
362
|
++
req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None)
|
|
363
|
++
await full_node_api.reorg_from_index_to_new_index(req)
|
|
364
|
++
|
|
365
|
++
coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
|
|
366
|
++
assert coin_records == []
|
|
449
367
|
|
|
450
|
--
|
|
451
|
--
req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None)
|
|
452
|
--
await full_node_api.reorg_from_index_to_new_index(req)
|
|
368
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
453
369
|
|
|
454
|
--
|
|
455
|
--
|
|
370
|
++
coin_update_messages = []
|
|
371
|
++
for message in all_messages:
|
|
372
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
373
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
374
|
++
coin_update_messages.append(data_response)
|
|
375
|
++
|
|
376
|
++
# First state is creation, second one is a reorg
|
|
377
|
++
assert len(coin_update_messages) == 2
|
|
378
|
++
first = coin_update_messages[0]
|
|
456
379
|
|
|
457
|
--
|
|
380
|
++
assert len(first.items) == 2
|
|
381
|
++
first_state_coin_1 = first.items[0]
|
|
382
|
++
assert first_state_coin_1.spent_height is None
|
|
383
|
++
assert first_state_coin_1.created_height is not None
|
|
384
|
++
first_state_coin_2 = first.items[1]
|
|
385
|
++
assert first_state_coin_2.spent_height is None
|
|
386
|
++
assert first_state_coin_2.created_height is not None
|
|
387
|
++
|
|
388
|
++
second = coin_update_messages[1]
|
|
389
|
++
assert second.fork_height == fork_height
|
|
390
|
++
assert len(second.items) == 2
|
|
391
|
++
second_state_coin_1 = second.items[0]
|
|
392
|
++
assert second_state_coin_1.spent_height is None
|
|
393
|
++
assert second_state_coin_1.created_height is None
|
|
394
|
++
second_state_coin_2 = second.items[1]
|
|
395
|
++
assert second_state_coin_2.spent_height is None
|
|
396
|
++
assert second_state_coin_2.created_height is None
|
|
397
|
++
|
|
398
|
++
|
|
399
|
++
@pytest.mark.anyio
|
|
400
|
++
async def test_subscribe_for_coin_id_reorg(simulator_and_wallet, self_hostname):
|
|
401
|
++
num_blocks = 4
|
|
402
|
++
long_blocks = 20
|
|
403
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
404
|
++
full_node_api = full_nodes[0]
|
|
405
|
++
wallet_node, server_2 = wallets[0]
|
|
406
|
++
fn_server = full_node_api.full_node.server
|
|
407
|
++
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
408
|
++
standard_wallet: Wallet = wsm.wallets[1]
|
|
409
|
++
puzzle_hash = await standard_wallet.get_new_puzzlehash()
|
|
410
|
++
|
|
411
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
412
|
++
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
413
|
++
|
|
414
|
++
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
415
|
++
|
|
416
|
++
# Farm to create a coin that we'll track
|
|
417
|
++
for i in range(0, num_blocks):
|
|
418
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
419
|
++
|
|
420
|
++
for i in range(0, long_blocks):
|
|
421
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
422
|
++
|
|
423
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(puzzle_hash))
|
|
424
|
++
|
|
425
|
++
for i in range(0, num_blocks):
|
|
426
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(zero_ph))
|
|
427
|
++
|
|
428
|
++
expected_height = uint32(long_blocks + 2 * num_blocks + 1)
|
|
429
|
++
await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, expected_height)
|
|
430
|
++
|
|
431
|
++
coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
|
|
432
|
++
assert len(coin_records) > 0
|
|
458
433
|
|
|
459
|
--
|
|
460
|
--
|
|
461
|
--
|
|
462
|
--
|
|
463
|
--
coin_update_messages.append(data_response)
|
|
464
|
--
|
|
465
|
--
assert len(coin_update_messages) == 1
|
|
466
|
--
update = coin_update_messages[0]
|
|
467
|
--
coin_states = update.items
|
|
468
|
--
assert len(coin_states) == 2
|
|
469
|
--
first_coin = coin_states[0]
|
|
470
|
--
assert first_coin.spent_height is None
|
|
471
|
--
assert first_coin.created_height is None
|
|
472
|
--
second_coin = coin_states[1]
|
|
473
|
--
assert second_coin.spent_height is None
|
|
474
|
--
assert second_coin.created_height is None
|
|
475
|
--
|
|
476
|
--
@pytest.mark.anyio
|
|
477
|
--
async def test_subscribe_for_hint(self, simulator_and_wallet, self_hostname):
|
|
478
|
--
num_blocks = 4
|
|
479
|
--
full_nodes, wallets, bt = simulator_and_wallet
|
|
480
|
--
full_node_api = full_nodes[0]
|
|
481
|
--
wallet_node, server_2 = wallets[0]
|
|
482
|
--
fn_server = full_node_api.full_node.server
|
|
483
|
--
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
484
|
--
|
|
485
|
--
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
486
|
--
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
487
|
--
|
|
488
|
--
wt: WalletTool = bt.get_pool_wallet_tool()
|
|
489
|
--
ph = wt.get_new_puzzlehash()
|
|
490
|
--
for i in range(0, num_blocks):
|
|
491
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
492
|
--
|
|
493
|
--
await asyncio.sleep(6)
|
|
494
|
--
coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
|
|
495
|
--
coin_spent = coins[0].coin
|
|
496
|
--
hint_puzzle_hash = 32 * b"\2"
|
|
497
|
--
amount = 1
|
|
498
|
--
amount_bin = int_to_bytes(1)
|
|
499
|
--
hint = 32 * b"\5"
|
|
500
|
--
|
|
501
|
--
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
502
|
--
msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
|
|
503
|
--
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
504
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
505
|
--
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
506
|
--
assert len(data_response.coin_states) == 0
|
|
507
|
--
|
|
508
|
--
condition_dict = {
|
|
509
|
--
ConditionOpcode.CREATE_COIN: [
|
|
510
|
--
ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
|
|
511
|
--
]
|
|
512
|
--
}
|
|
513
|
--
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
514
|
--
|
|
515
|
--
tx: SpendBundle = wt.generate_signed_transaction(
|
|
516
|
--
10,
|
|
517
|
--
wt.get_new_puzzlehash(),
|
|
518
|
--
coin_spent,
|
|
519
|
--
condition_dic=condition_dict,
|
|
520
|
--
)
|
|
521
|
--
await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
|
|
522
|
--
|
|
523
|
--
await full_node_api.process_spend_bundles(bundles=[tx])
|
|
524
|
--
|
|
525
|
--
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
434
|
++
for coin_rec in coin_records:
|
|
435
|
++
msg = wallet_protocol.RegisterForCoinUpdates([coin_rec.name], 0)
|
|
436
|
++
msg_response = await full_node_api.register_interest_in_coin(msg, fake_wallet_peer)
|
|
437
|
++
assert msg_response is not None
|
|
526
438
|
|
|
439
|
++
fork_height = expected_height - num_blocks - 5
|
|
440
|
++
req = ReorgProtocol(fork_height, expected_height + 5, zero_ph, None)
|
|
441
|
++
await full_node_api.reorg_from_index_to_new_index(req)
|
|
442
|
++
|
|
443
|
++
coin_records = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hash(True, puzzle_hash)
|
|
444
|
++
assert coin_records == []
|
|
445
|
++
|
|
446
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
447
|
++
|
|
448
|
++
coin_update_messages = []
|
|
449
|
++
for message in all_messages:
|
|
450
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
451
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
452
|
++
coin_update_messages.append(data_response)
|
|
453
|
++
|
|
454
|
++
assert len(coin_update_messages) == 1
|
|
455
|
++
update = coin_update_messages[0]
|
|
456
|
++
coin_states = update.items
|
|
457
|
++
assert len(coin_states) == 2
|
|
458
|
++
first_coin = coin_states[0]
|
|
459
|
++
assert first_coin.spent_height is None
|
|
460
|
++
assert first_coin.created_height is None
|
|
461
|
++
second_coin = coin_states[1]
|
|
462
|
++
assert second_coin.spent_height is None
|
|
463
|
++
assert second_coin.created_height is None
|
|
464
|
++
|
|
465
|
++
|
|
466
|
++
@pytest.mark.anyio
|
|
467
|
++
async def test_subscribe_for_hint(simulator_and_wallet, self_hostname):
|
|
468
|
++
num_blocks = 4
|
|
469
|
++
full_nodes, wallets, bt = simulator_and_wallet
|
|
470
|
++
full_node_api = full_nodes[0]
|
|
471
|
++
wallet_node, server_2 = wallets[0]
|
|
472
|
++
fn_server = full_node_api.full_node.server
|
|
473
|
++
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
474
|
++
|
|
475
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
476
|
++
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
477
|
++
|
|
478
|
++
wt: WalletTool = bt.get_pool_wallet_tool()
|
|
479
|
++
ph = wt.get_new_puzzlehash()
|
|
480
|
++
for i in range(0, num_blocks):
|
|
481
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
482
|
++
|
|
483
|
++
await asyncio.sleep(6)
|
|
484
|
++
coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
|
|
485
|
++
coin_spent = coins[0].coin
|
|
486
|
++
hint_puzzle_hash = 32 * b"\2"
|
|
487
|
++
amount = 1
|
|
488
|
++
amount_bin = int_to_bytes(1)
|
|
489
|
++
hint = 32 * b"\5"
|
|
490
|
++
|
|
491
|
++
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
492
|
++
msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
|
|
493
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
494
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
495
|
++
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
496
|
++
assert len(data_response.coin_states) == 0
|
|
497
|
++
|
|
498
|
++
condition_dict = {
|
|
499
|
++
ConditionOpcode.CREATE_COIN: [
|
|
500
|
++
ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
|
|
501
|
++
]
|
|
502
|
++
}
|
|
503
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
504
|
++
|
|
505
|
++
tx: SpendBundle = wt.generate_signed_transaction(
|
|
506
|
++
10,
|
|
507
|
++
wt.get_new_puzzlehash(),
|
|
508
|
++
coin_spent,
|
|
509
|
++
condition_dic=condition_dict,
|
|
510
|
++
)
|
|
511
|
++
await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
|
|
512
|
++
|
|
513
|
++
await full_node_api.process_spend_bundles(bundles=[tx])
|
|
514
|
++
|
|
515
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
516
|
++
|
|
517
|
++
notified_state = None
|
|
518
|
++
|
|
519
|
++
for message in all_messages:
|
|
520
|
++
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
521
|
++
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
522
|
++
notified_state = data_response
|
|
523
|
++
break
|
|
524
|
++
|
|
525
|
++
assert notified_state is not None
|
|
526
|
++
assert notified_state.items[0].coin == Coin(coin_spent.name(), hint_puzzle_hash, amount)
|
|
527
|
++
|
|
528
|
++
msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
|
|
529
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
530
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
531
|
++
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
532
|
++
# we have already subscribed to this puzzle hash. The full node will
|
|
533
|
++
# ignore the duplicate
|
|
534
|
++
assert data_response.coin_states == []
|
|
535
|
++
|
|
536
|
++
|
|
537
|
++
@pytest.mark.anyio
|
|
538
|
++
async def test_subscribe_for_puzzle_hash_coin_hint_duplicates(
|
|
539
|
++
simulator_and_wallet: OldSimulatorsAndWallets, self_hostname: str
|
|
540
|
++
) -> None:
|
|
541
|
++
[full_node_api], [[_, wallet_server]], bt = simulator_and_wallet
|
|
542
|
++
full_node_server = full_node_api.full_node.server
|
|
543
|
++
|
|
544
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
545
|
++
|
|
546
|
++
wt: WalletTool = bt.get_pool_wallet_tool()
|
|
547
|
++
ph = wt.get_new_puzzlehash()
|
|
548
|
++
await full_node_api.farm_blocks_to_puzzlehash(4, ph)
|
|
549
|
++
coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
|
|
550
|
++
wallet_connection = full_node_server.all_connections[wallet_server.node_id]
|
|
551
|
++
|
|
552
|
++
# Create a coin which is hinted with its own destination puzzle hash
|
|
553
|
++
tx: SpendBundle = wt.generate_signed_transaction(
|
|
554
|
++
uint64(10),
|
|
555
|
++
wt.get_new_puzzlehash(),
|
|
556
|
++
coins[0].coin,
|
|
557
|
++
condition_dic={
|
|
558
|
++
ConditionOpcode.CREATE_COIN: [ConditionWithArgs(ConditionOpcode.CREATE_COIN, [ph, int_to_bytes(1), ph])]
|
|
559
|
++
},
|
|
560
|
++
)
|
|
561
|
++
await full_node_api.respond_transaction(RespondTransaction(tx), wallet_connection)
|
|
562
|
++
await full_node_api.process_spend_bundles(bundles=[tx])
|
|
563
|
++
# Query the coin states and make sure it doesn't contain duplicated entries
|
|
564
|
++
msg = wallet_protocol.RegisterForPhUpdates([ph], uint32(0))
|
|
565
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, wallet_connection)
|
|
566
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
567
|
++
response = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
568
|
++
assert len(response.coin_states) > 0
|
|
569
|
++
assert len(set(response.coin_states)) == len(response.coin_states)
|
|
570
|
++
|
|
571
|
++
|
|
572
|
++
@pytest.mark.anyio
|
|
573
|
++
async def test_subscribe_for_hint_long_sync(wallet_two_node_simulator, self_hostname):
|
|
574
|
++
num_blocks = 4
|
|
575
|
++
full_nodes, wallets, bt = wallet_two_node_simulator
|
|
576
|
++
full_node_api = full_nodes[0]
|
|
577
|
++
full_node_api_1 = full_nodes[1]
|
|
578
|
++
|
|
579
|
++
wallet_node, server_2 = wallets[0]
|
|
580
|
++
fn_server = full_node_api.full_node.server
|
|
581
|
++
fn_server_1 = full_node_api_1.full_node.server
|
|
582
|
++
|
|
583
|
++
wsm: WalletStateManager = wallet_node.wallet_state_manager
|
|
584
|
++
|
|
585
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
586
|
++
incoming_queue, peer_id = await add_dummy_connection(fn_server, self_hostname, 12312, NodeType.WALLET)
|
|
587
|
++
incoming_queue_1, peer_id_1 = await add_dummy_connection(fn_server_1, self_hostname, 12313, NodeType.WALLET)
|
|
588
|
++
|
|
589
|
++
wt: WalletTool = bt.get_pool_wallet_tool()
|
|
590
|
++
ph = wt.get_new_puzzlehash()
|
|
591
|
++
for i in range(0, num_blocks):
|
|
592
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
593
|
++
|
|
594
|
++
await asyncio.sleep(6)
|
|
595
|
++
coins = await full_node_api.full_node.coin_store.get_coin_records_by_puzzle_hashes(False, [ph])
|
|
596
|
++
coin_spent = coins[0].coin
|
|
597
|
++
hint_puzzle_hash = 32 * b"\2"
|
|
598
|
++
amount = 1
|
|
599
|
++
amount_bin = int_to_bytes(1)
|
|
600
|
++
hint = 32 * b"\5"
|
|
601
|
++
|
|
602
|
++
fake_wallet_peer = fn_server.all_connections[peer_id]
|
|
603
|
++
fake_wallet_peer_1 = fn_server_1.all_connections[peer_id_1]
|
|
604
|
++
msg = wallet_protocol.RegisterForPhUpdates([hint], 0)
|
|
605
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, fake_wallet_peer)
|
|
606
|
++
msg_response_1 = await full_node_api_1.register_interest_in_puzzle_hash(msg, fake_wallet_peer_1)
|
|
607
|
++
|
|
608
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
609
|
++
data_response: RespondToPhUpdates = RespondToCoinUpdates.from_bytes(msg_response.data)
|
|
610
|
++
assert len(data_response.coin_states) == 0
|
|
611
|
++
|
|
612
|
++
condition_dict = {
|
|
613
|
++
ConditionOpcode.CREATE_COIN: [
|
|
614
|
++
ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
|
|
615
|
++
]
|
|
616
|
++
}
|
|
617
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
618
|
++
|
|
619
|
++
tx: SpendBundle = wt.generate_signed_transaction(
|
|
620
|
++
10,
|
|
621
|
++
wt.get_new_puzzlehash(),
|
|
622
|
++
coin_spent,
|
|
623
|
++
condition_dic=condition_dict,
|
|
624
|
++
)
|
|
625
|
++
await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
|
|
626
|
++
|
|
627
|
++
await full_node_api.process_spend_bundles(bundles=[tx])
|
|
628
|
++
|
|
629
|
++
# Create more blocks than recent "short_sync_blocks_behind_threshold" so that node enters batch
|
|
630
|
++
for i in range(0, 100):
|
|
631
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
632
|
++
|
|
633
|
++
node1_height = full_node_api_1.full_node.blockchain.get_peak_height()
|
|
634
|
++
assert node1_height is None
|
|
635
|
++
|
|
636
|
++
await fn_server_1.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
637
|
++
node0_height = full_node_api.full_node.blockchain.get_peak_height()
|
|
638
|
++
await time_out_assert(60, full_node_api_1.full_node.blockchain.get_peak_height, node0_height)
|
|
639
|
++
|
|
640
|
++
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
641
|
++
all_messages_1 = await get_all_messages_in_queue(incoming_queue_1)
|
|
642
|
++
|
|
643
|
++
def check_messages_for_hint(messages):
|
|
527
644
|
notified_state = None
|
|
528
645
|
|
|
529
|
--
for message in
|
|
646
|
++
for message in messages:
|
|
530
647
|
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
531
648
|
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
532
649
|
notified_state = data_response
|
|
@@@ -535,210 -535,210 +652,87 @@@
|
|
|
535
652
|
assert notified_state is not None
|
|
536
653
|
assert notified_state.items[0].coin == Coin(coin_spent.name(), hint_puzzle_hash, amount)
|
|
537
654
|
|
|
538
|
--
|
|
539
|
--
|
|
540
|
--
|
|
541
|
--
|
|
542
|
--
|
|
543
|
--
|
|
544
|
--
|
|
545
|
--
|
|
546
|
--
|
|
547
|
--
|
|
548
|
--
|
|
549
|
--
|
|
550
|
--
|
|
551
|
--
|
|
552
|
--
|
|
553
|
--
|
|
554
|
--
|
|
555
|
--
|
|
556
|
--
|
|
557
|
--
|
|
558
|
--
|
|
559
|
--
|
|
560
|
--
|
|
561
|
--
|
|
562
|
--
|
|
563
|
--
|
|
564
|
--
|
|
565
|
--
|
|
566
|
--
|
|
567
|
--
|
|
568
|
--
|
|
569
|
--
|
|
570
|
--
|
|
571
|
--
|
|
572
|
--
|
|
573
|
--
|
|
574
|
--
|
|
575
|
--
|
|
576
|
--
|
|
577
|
--
|
|
578
|
--
|
|
579
|
--
|
|
580
|
--
|
|
581
|
--
|
|
582
|
--
|
|
583
|
--
|
|
584
|
--
|
|
585
|
--
|
|
586
|
--
|
|
587
|
--
|
|
588
|
--
|
|
589
|
--
|
|
590
|
--
|
|
591
|
--
|
|
592
|
--
|
|
593
|
--
|
|
594
|
--
|
|
595
|
--
|
|
596
|
--
|
|
597
|
--
|
|
598
|
--
|
|
599
|
--
|
|
600
|
--
|
|
601
|
--
|
|
602
|
--
|
|
603
|
--
|
|
604
|
--
|
|
605
|
--
|
|
606
|
--
|
|
607
|
--
|
|
608
|
--
|
|
609
|
--
|
|
610
|
--
|
|
611
|
--
|
|
612
|
--
|
|
613
|
--
|
|
614
|
--
|
|
615
|
--
|
|
616
|
--
|
|
617
|
--
|
|
618
|
--
|
|
619
|
--
|
|
620
|
--
|
|
621
|
--
|
|
622
|
--
ConditionWithArgs(ConditionOpcode.CREATE_COIN, [hint_puzzle_hash, amount_bin, hint])
|
|
623
|
--
]
|
|
624
|
--
}
|
|
625
|
--
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
626
|
--
|
|
627
|
--
tx: SpendBundle = wt.generate_signed_transaction(
|
|
628
|
--
10,
|
|
629
|
--
wt.get_new_puzzlehash(),
|
|
630
|
--
coin_spent,
|
|
631
|
--
condition_dic=condition_dict,
|
|
632
|
--
)
|
|
633
|
--
await full_node_api.respond_transaction(RespondTransaction(tx), fake_wallet_peer)
|
|
634
|
--
|
|
635
|
--
await full_node_api.process_spend_bundles(bundles=[tx])
|
|
636
|
--
|
|
637
|
--
# Create more blocks than recent "short_sync_blocks_behind_threshold" so that node enters batch
|
|
638
|
--
for i in range(0, 100):
|
|
639
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
640
|
--
|
|
641
|
--
node1_height = full_node_api_1.full_node.blockchain.get_peak_height()
|
|
642
|
--
assert node1_height is None
|
|
643
|
--
|
|
644
|
--
await fn_server_1.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
645
|
--
node0_height = full_node_api.full_node.blockchain.get_peak_height()
|
|
646
|
--
await time_out_assert(60, full_node_api_1.full_node.blockchain.get_peak_height, node0_height)
|
|
647
|
--
|
|
648
|
--
all_messages = await get_all_messages_in_queue(incoming_queue)
|
|
649
|
--
all_messages_1 = await get_all_messages_in_queue(incoming_queue_1)
|
|
650
|
--
|
|
651
|
--
def check_messages_for_hint(messages):
|
|
652
|
--
notified_state = None
|
|
653
|
--
|
|
654
|
--
for message in messages:
|
|
655
|
--
if message.type == ProtocolMessageTypes.coin_state_update.value:
|
|
656
|
--
data_response: CoinStateUpdate = CoinStateUpdate.from_bytes(message.data)
|
|
657
|
--
notified_state = data_response
|
|
658
|
--
break
|
|
659
|
--
|
|
660
|
--
assert notified_state is not None
|
|
661
|
--
assert notified_state.items[0].coin == Coin(coin_spent.name(), hint_puzzle_hash, amount)
|
|
662
|
--
|
|
663
|
--
check_messages_for_hint(all_messages)
|
|
664
|
--
check_messages_for_hint(all_messages_1)
|
|
665
|
--
|
|
666
|
--
@pytest.mark.anyio
|
|
667
|
--
async def test_ph_subscribe_limits(self, simulator_and_wallet, self_hostname):
|
|
668
|
--
full_nodes, wallets, _ = simulator_and_wallet
|
|
669
|
--
full_node_api = full_nodes[0]
|
|
670
|
--
wallet_node, server_2 = wallets[0]
|
|
671
|
--
fn_server = full_node_api.full_node.server
|
|
672
|
--
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
673
|
--
con = list(fn_server.all_connections.values())[0]
|
|
674
|
--
phs = []
|
|
675
|
--
phs.append(32 * b"\0")
|
|
676
|
--
phs.append(32 * b"\1")
|
|
677
|
--
phs.append(32 * b"\2")
|
|
678
|
--
phs.append(32 * b"\3")
|
|
679
|
--
phs.append(32 * b"\4")
|
|
680
|
--
phs.append(32 * b"\5")
|
|
681
|
--
phs.append(32 * b"\6")
|
|
682
|
--
full_node_api.full_node.config["max_subscribe_items"] = 2
|
|
683
|
--
assert full_node_api.is_trusted(con) is False
|
|
684
|
--
msg = wallet_protocol.RegisterForPhUpdates(phs, 0)
|
|
685
|
--
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
|
|
686
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
687
|
--
s = full_node_api.full_node.subscriptions
|
|
688
|
--
assert len(s._ph_subscriptions) == 2
|
|
689
|
--
assert s.has_ph_subscription(phs[0])
|
|
690
|
--
assert s.has_ph_subscription(phs[1])
|
|
691
|
--
assert not s.has_ph_subscription(phs[2])
|
|
692
|
--
assert not s.has_ph_subscription(phs[3])
|
|
693
|
--
full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
|
|
694
|
--
full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
|
|
695
|
--
assert full_node_api.is_trusted(con) is True
|
|
696
|
--
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
|
|
697
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
698
|
--
assert len(s._ph_subscriptions) == 4
|
|
699
|
--
assert s.has_ph_subscription(phs[0])
|
|
700
|
--
assert s.has_ph_subscription(phs[1])
|
|
701
|
--
assert s.has_ph_subscription(phs[2])
|
|
702
|
--
assert s.has_ph_subscription(phs[3])
|
|
703
|
--
assert not s.has_ph_subscription(phs[4])
|
|
704
|
--
assert not s.has_ph_subscription(phs[5])
|
|
705
|
--
|
|
706
|
--
@pytest.mark.anyio
|
|
707
|
--
async def test_coin_subscribe_limits(self, simulator_and_wallet, self_hostname):
|
|
708
|
--
full_nodes, wallets, _ = simulator_and_wallet
|
|
709
|
--
full_node_api = full_nodes[0]
|
|
710
|
--
wallet_node, server_2 = wallets[0]
|
|
711
|
--
fn_server = full_node_api.full_node.server
|
|
712
|
--
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
713
|
--
con = list(fn_server.all_connections.values())[0]
|
|
714
|
--
coins = []
|
|
715
|
--
coins.append(32 * b"\0")
|
|
716
|
--
coins.append(32 * b"\1")
|
|
717
|
--
coins.append(32 * b"\2")
|
|
718
|
--
coins.append(32 * b"\3")
|
|
719
|
--
coins.append(32 * b"\4")
|
|
720
|
--
coins.append(32 * b"\5")
|
|
721
|
--
coins.append(32 * b"\6")
|
|
722
|
--
full_node_api.full_node.config["max_subscribe_items"] = 2
|
|
723
|
--
assert full_node_api.is_trusted(con) is False
|
|
724
|
--
msg = wallet_protocol.RegisterForCoinUpdates(coins, 0)
|
|
725
|
--
msg_response = await full_node_api.register_interest_in_coin(msg, con)
|
|
726
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
727
|
--
s = full_node_api.full_node.subscriptions
|
|
728
|
--
assert len(s._coin_subscriptions) == 2
|
|
729
|
--
assert s.has_coin_subscription(coins[0])
|
|
730
|
--
assert s.has_coin_subscription(coins[1])
|
|
731
|
--
assert not s.has_coin_subscription(coins[2])
|
|
732
|
--
assert not s.has_coin_subscription(coins[3])
|
|
733
|
--
full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
|
|
734
|
--
full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
|
|
735
|
--
assert full_node_api.is_trusted(con) is True
|
|
736
|
--
msg_response = await full_node_api.register_interest_in_coin(msg, con)
|
|
737
|
--
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
738
|
--
assert len(s._coin_subscriptions) == 4
|
|
739
|
--
assert s.has_coin_subscription(coins[0])
|
|
740
|
--
assert s.has_coin_subscription(coins[1])
|
|
741
|
--
assert s.has_coin_subscription(coins[2])
|
|
742
|
--
assert s.has_coin_subscription(coins[3])
|
|
743
|
--
assert not s.has_coin_subscription(coins[4])
|
|
744
|
--
assert not s.has_coin_subscription(coins[5])
|
|
655
|
++
check_messages_for_hint(all_messages)
|
|
656
|
++
check_messages_for_hint(all_messages_1)
|
|
657
|
++
|
|
658
|
++
|
|
659
|
++
@pytest.mark.anyio
|
|
660
|
++
async def test_ph_subscribe_limits(simulator_and_wallet, self_hostname):
|
|
661
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
662
|
++
full_node_api = full_nodes[0]
|
|
663
|
++
wallet_node, server_2 = wallets[0]
|
|
664
|
++
fn_server = full_node_api.full_node.server
|
|
665
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
666
|
++
con = list(fn_server.all_connections.values())[0]
|
|
667
|
++
phs = []
|
|
668
|
++
phs.append(32 * b"\0")
|
|
669
|
++
phs.append(32 * b"\1")
|
|
670
|
++
phs.append(32 * b"\2")
|
|
671
|
++
phs.append(32 * b"\3")
|
|
672
|
++
phs.append(32 * b"\4")
|
|
673
|
++
phs.append(32 * b"\5")
|
|
674
|
++
phs.append(32 * b"\6")
|
|
675
|
++
full_node_api.full_node.config["max_subscribe_items"] = 2
|
|
676
|
++
assert full_node_api.is_trusted(con) is False
|
|
677
|
++
msg = wallet_protocol.RegisterForPhUpdates(phs, 0)
|
|
678
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
|
|
679
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
680
|
++
s = full_node_api.full_node.subscriptions
|
|
681
|
++
assert s.puzzle_subscription_count() == 2
|
|
682
|
++
assert s.has_puzzle_subscription(phs[0])
|
|
683
|
++
assert s.has_puzzle_subscription(phs[1])
|
|
684
|
++
assert not s.has_puzzle_subscription(phs[2])
|
|
685
|
++
assert not s.has_puzzle_subscription(phs[3])
|
|
686
|
++
full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
|
|
687
|
++
full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
|
|
688
|
++
assert full_node_api.is_trusted(con) is True
|
|
689
|
++
msg_response = await full_node_api.register_interest_in_puzzle_hash(msg, con)
|
|
690
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_ph_update.value
|
|
691
|
++
assert s.puzzle_subscription_count() == 4
|
|
692
|
++
assert s.has_puzzle_subscription(phs[0])
|
|
693
|
++
assert s.has_puzzle_subscription(phs[1])
|
|
694
|
++
assert s.has_puzzle_subscription(phs[2])
|
|
695
|
++
assert s.has_puzzle_subscription(phs[3])
|
|
696
|
++
assert not s.has_puzzle_subscription(phs[4])
|
|
697
|
++
assert not s.has_puzzle_subscription(phs[5])
|
|
698
|
++
|
|
699
|
++
|
|
700
|
++
@pytest.mark.anyio
|
|
701
|
++
async def test_coin_subscribe_limits(simulator_and_wallet, self_hostname):
|
|
702
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
703
|
++
full_node_api = full_nodes[0]
|
|
704
|
++
wallet_node, server_2 = wallets[0]
|
|
705
|
++
fn_server = full_node_api.full_node.server
|
|
706
|
++
await server_2.start_client(PeerInfo(self_hostname, fn_server.get_port()), None)
|
|
707
|
++
con = list(fn_server.all_connections.values())[0]
|
|
708
|
++
coins = []
|
|
709
|
++
coins.append(32 * b"\0")
|
|
710
|
++
coins.append(32 * b"\1")
|
|
711
|
++
coins.append(32 * b"\2")
|
|
712
|
++
coins.append(32 * b"\3")
|
|
713
|
++
coins.append(32 * b"\4")
|
|
714
|
++
coins.append(32 * b"\5")
|
|
715
|
++
coins.append(32 * b"\6")
|
|
716
|
++
full_node_api.full_node.config["max_subscribe_items"] = 2
|
|
717
|
++
assert full_node_api.is_trusted(con) is False
|
|
718
|
++
msg = wallet_protocol.RegisterForCoinUpdates(coins, 0)
|
|
719
|
++
msg_response = await full_node_api.register_interest_in_coin(msg, con)
|
|
720
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
721
|
++
s = full_node_api.full_node.subscriptions
|
|
722
|
++
assert s.coin_subscription_count() == 2
|
|
723
|
++
assert s.has_coin_subscription(coins[0])
|
|
724
|
++
assert s.has_coin_subscription(coins[1])
|
|
725
|
++
assert not s.has_coin_subscription(coins[2])
|
|
726
|
++
assert not s.has_coin_subscription(coins[3])
|
|
727
|
++
full_node_api.full_node.config["trusted_max_subscribe_items"] = 4
|
|
728
|
++
full_node_api.full_node.config["trusted_peers"] = {server_2.node_id.hex(): server_2.node_id.hex()}
|
|
729
|
++
assert full_node_api.is_trusted(con) is True
|
|
730
|
++
msg_response = await full_node_api.register_interest_in_coin(msg, con)
|
|
731
|
++
assert msg_response.type == ProtocolMessageTypes.respond_to_coin_update.value
|
|
732
|
++
assert s.coin_subscription_count() == 4
|
|
733
|
++
assert s.has_coin_subscription(coins[0])
|
|
734
|
++
assert s.has_coin_subscription(coins[1])
|
|
735
|
++
assert s.has_coin_subscription(coins[2])
|
|
736
|
++
assert s.has_coin_subscription(coins[3])
|
|
737
|
++
assert not s.has_coin_subscription(coins[4])
|
|
738
|
++
assert not s.has_coin_subscription(coins[5])
|
|
@@@ -31,17 -31,17 +31,17 @@@ from chia.wallet.util.compute_memos imp
|
|
|
31
31
|
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
|
|
32
32
|
from chia.wallet.util.wallet_sync_utils import PeerRequestException
|
|
33
33
|
from chia.wallet.wallet_coin_record import WalletCoinRecord
|
|
34
|
++
from chia.wallet.wallet_state_manager import WalletStateManager
|
|
34
35
|
from chia.wallet.wallet_weight_proof_handler import get_wp_fork_point
|
|
35
36
|
from tests.connection_utils import disconnect_all, disconnect_all_and_reconnect
|
|
37
|
++
from tests.util.misc import wallet_height_at_least
|
|
36
38
|
from tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
|
|
37
39
|
from tests.weight_proof.test_weight_proof import load_blocks_dont_validate
|
|
38
40
|
|
|
39
41
|
|
|
40
|
--
async def
|
|
41
|
--
|
|
42
|
--
|
|
43
|
--
return True
|
|
44
|
--
return False
|
|
42
|
++
async def get_tx_count(wsm: WalletStateManager, wallet_id: int) -> int:
|
|
43
|
++
txs = await wsm.get_all_transactions(wallet_id)
|
|
44
|
++
return len(txs)
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
async def get_nft_count(wallet: NFTWallet) -> int:
|
|
@@@ -51,758 -51,758 +51,713 @@@
|
|
|
51
51
|
log = getLogger(__name__)
|
|
52
52
|
|
|
53
53
|
|
|
54
|
--
|
|
55
|
--
|
|
56
|
--
|
|
57
|
--
|
|
58
|
--
|
|
59
|
--
|
|
54
|
++
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
55
|
++
@pytest.mark.anyio
|
|
56
|
++
async def test_request_block_headers(simulator_and_wallet, default_1000_blocks):
|
|
57
|
++
# Tests the edge case of receiving funds right before the recent blocks in weight proof
|
|
58
|
++
[full_node_api], [(wallet_node, _)], bt = simulator_and_wallet
|
|
59
|
++
|
|
60
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
61
|
++
ph = await wallet.get_new_puzzlehash()
|
|
62
|
++
for block in default_1000_blocks[:100]:
|
|
63
|
++
await full_node_api.full_node.add_block(block)
|
|
64
|
++
|
|
65
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(15), False))
|
|
66
|
++
assert msg.type == ProtocolMessageTypes.respond_block_headers.value
|
|
67
|
++
res_block_headers = RespondBlockHeaders.from_bytes(msg.data)
|
|
68
|
++
bh = res_block_headers.header_blocks
|
|
69
|
++
assert len(bh) == 6
|
|
70
|
++
assert [x.reward_chain_block.height for x in default_1000_blocks[10:16]] == [
|
|
71
|
++
x.reward_chain_block.height for x in bh
|
|
72
|
++
]
|
|
73
|
++
|
|
74
|
++
assert [x.foliage for x in default_1000_blocks[10:16]] == [x.foliage for x in bh]
|
|
75
|
++
|
|
76
|
++
assert [x.transactions_filter for x in bh] == [b"\x00"] * 6
|
|
77
|
++
|
|
78
|
++
num_blocks = 20
|
|
79
|
++
new_blocks = bt.get_consecutive_blocks(num_blocks, block_list_input=default_1000_blocks, pool_reward_puzzle_hash=ph)
|
|
80
|
++
for i in range(0, len(new_blocks)):
|
|
81
|
++
await full_node_api.full_node.add_block(new_blocks[i])
|
|
82
|
++
|
|
83
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(110), uint32(115), True))
|
|
84
|
++
res_block_headers = RespondBlockHeaders.from_bytes(msg.data)
|
|
85
|
++
bh = res_block_headers.header_blocks
|
|
86
|
++
assert len(bh) == 6
|
|
87
|
++
|
|
88
|
++
|
|
89
|
++
# @pytest.mark.parametrize(
|
|
90
|
++
# "test_case",
|
|
91
|
++
# [(1000000, 10000010, False, ProtocolMessageTypes.reject_block_headers)],
|
|
92
|
++
# [(80, 99, False, ProtocolMessageTypes.respond_block_headers)],
|
|
93
|
++
# [(10, 8, False, None)],
|
|
94
|
++
# )
|
|
95
|
++
@pytest.mark.anyio
|
|
96
|
++
async def test_request_block_headers_rejected(simulator_and_wallet, default_1000_blocks):
|
|
97
|
++
# Tests the edge case of receiving funds right before the recent blocks in weight proof
|
|
98
|
++
[full_node_api], _, bt = simulator_and_wallet
|
|
99
|
++
|
|
100
|
++
# start_height, end_height, return_filter, expected_res = test_case
|
|
101
|
++
|
|
102
|
++
msg = await full_node_api.request_block_headers(
|
|
103
|
++
wallet_protocol.RequestBlockHeaders(uint32(1000000), uint32(1000010), False)
|
|
104
|
++
)
|
|
105
|
++
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
106
|
++
|
|
107
|
++
for block in default_1000_blocks[:150]:
|
|
108
|
++
await full_node_api.full_node.add_block(block)
|
|
109
|
++
|
|
110
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(80), uint32(99), False))
|
|
111
|
++
assert msg.type == ProtocolMessageTypes.respond_block_headers.value
|
|
112
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(8), False))
|
|
113
|
++
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
114
|
++
|
|
115
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(8), True))
|
|
116
|
++
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
117
|
++
|
|
118
|
++
# test for 128 blocks to fetch at once limit
|
|
119
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(10), uint32(140), True))
|
|
120
|
++
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
121
|
++
|
|
122
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(90), uint32(160), False))
|
|
123
|
++
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
124
|
++
msg = await full_node_api.request_block_headers(wallet_protocol.RequestBlockHeaders(uint32(90), uint32(160), True))
|
|
125
|
++
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
126
|
++
|
|
127
|
++
|
|
128
|
++
@pytest.mark.parametrize(
|
|
129
|
++
"two_wallet_nodes",
|
|
130
|
++
[
|
|
131
|
++
dict(
|
|
132
|
++
disable_capabilities=[Capability.BLOCK_HEADERS],
|
|
133
|
++
),
|
|
134
|
++
dict(
|
|
135
|
++
disable_capabilities=[Capability.BASE],
|
|
136
|
++
),
|
|
137
|
++
],
|
|
138
|
++
indirect=True,
|
|
139
|
++
)
|
|
140
|
++
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
141
|
++
@pytest.mark.anyio
|
|
142
|
++
async def test_basic_sync_wallet(two_wallet_nodes, default_400_blocks, self_hostname):
|
|
143
|
++
full_nodes, wallets, bt = two_wallet_nodes
|
|
144
|
++
full_node_api = full_nodes[0]
|
|
145
|
++
full_node_server = full_node_api.full_node.server
|
|
146
|
++
|
|
147
|
++
# Trusted node sync
|
|
148
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
149
|
++
|
|
150
|
++
# Untrusted node sync
|
|
151
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
152
|
++
|
|
153
|
++
for block in default_400_blocks:
|
|
154
|
++
await full_node_api.full_node.add_block(block)
|
|
155
|
++
|
|
156
|
++
for wallet_node, wallet_server in wallets:
|
|
157
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
60
158
|
|
|
61
|
--
|
|
62
|
--
|
|
63
|
--
for block in default_1000_blocks[:100]:
|
|
64
|
--
await full_node_api.full_node.add_block(block)
|
|
159
|
++
for wallet_node, wallet_server in wallets:
|
|
160
|
++
await time_out_assert(100, wallet_height_at_least, True, wallet_node, len(default_400_blocks) - 1)
|
|
65
161
|
|
|
66
|
--
|
|
67
|
--
|
|
162
|
++
# Tests a reorg with the wallet
|
|
163
|
++
num_blocks = 30
|
|
164
|
++
blocks_reorg = bt.get_consecutive_blocks(num_blocks - 1, block_list_input=default_400_blocks[:-5])
|
|
165
|
++
blocks_reorg = bt.get_consecutive_blocks(1, blocks_reorg, guarantee_transaction_block=True, current_time=True)
|
|
166
|
++
for i in range(1, len(blocks_reorg)):
|
|
167
|
++
await full_node_api.full_node.add_block(blocks_reorg[i])
|
|
168
|
++
|
|
169
|
++
for wallet_node, wallet_server in wallets:
|
|
170
|
++
await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
|
|
171
|
++
|
|
172
|
++
for wallet_node, wallet_server in wallets:
|
|
173
|
++
await time_out_assert(
|
|
174
|
++
100, wallet_height_at_least, True, wallet_node, len(default_400_blocks) + num_blocks - 5 - 1
|
|
68
175
|
)
|
|
69
|
--
|
|
70
|
--
|
|
71
|
--
|
|
72
|
--
|
|
73
|
--
|
|
74
|
--
|
|
75
|
--
|
|
76
|
--
|
|
77
|
--
|
|
176
|
++
await time_out_assert(20, wallet_node.wallet_state_manager.synced)
|
|
177
|
++
await disconnect_all(wallet_server)
|
|
178
|
++
assert not (await wallet_node.wallet_state_manager.synced())
|
|
179
|
++
|
|
180
|
++
|
|
181
|
++
@pytest.mark.parametrize(
|
|
182
|
++
"two_wallet_nodes",
|
|
183
|
++
[
|
|
184
|
++
dict(
|
|
185
|
++
disable_capabilities=[Capability.BLOCK_HEADERS],
|
|
186
|
++
),
|
|
187
|
++
dict(
|
|
188
|
++
disable_capabilities=[Capability.BASE],
|
|
189
|
++
),
|
|
190
|
++
],
|
|
191
|
++
indirect=True,
|
|
192
|
++
)
|
|
193
|
++
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
194
|
++
@pytest.mark.anyio
|
|
195
|
++
async def test_almost_recent(two_wallet_nodes, default_400_blocks, self_hostname, blockchain_constants):
|
|
196
|
++
# Tests the edge case of receiving funds right before the recent blocks in weight proof
|
|
197
|
++
full_nodes, wallets, bt = two_wallet_nodes
|
|
198
|
++
full_node_api = full_nodes[0]
|
|
199
|
++
full_node_server = full_node_api.full_node.server
|
|
200
|
++
|
|
201
|
++
# Trusted node sync
|
|
202
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
203
|
++
|
|
204
|
++
# Untrusted node sync
|
|
205
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
206
|
++
|
|
207
|
++
base_num_blocks = 400
|
|
208
|
++
for block in default_400_blocks:
|
|
209
|
++
await full_node_api.full_node.add_block(block)
|
|
210
|
++
all_blocks = default_400_blocks
|
|
211
|
++
both_phs = []
|
|
212
|
++
for wallet_node, wallet_server in wallets:
|
|
213
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
214
|
++
both_phs.append(await wallet.get_new_puzzlehash())
|
|
78
215
|
|
|
79
|
--
|
|
216
|
++
for i in range(20):
|
|
217
|
++
# Tests a reorg with the wallet
|
|
218
|
++
ph = both_phs[i % 2]
|
|
219
|
++
all_blocks = bt.get_consecutive_blocks(1, block_list_input=all_blocks, pool_reward_puzzle_hash=ph)
|
|
220
|
++
await full_node_api.full_node.add_block(all_blocks[-1])
|
|
80
221
|
|
|
81
|
--
|
|
82
|
--
|
|
83
|
--
|
|
84
|
--
|
|
85
|
--
|
|
86
|
--
await full_node_api.full_node.add_block(new_blocks[i])
|
|
222
|
++
new_blocks = bt.get_consecutive_blocks(
|
|
223
|
++
blockchain_constants.WEIGHT_PROOF_RECENT_BLOCKS + 10, block_list_input=all_blocks
|
|
224
|
++
)
|
|
225
|
++
for i in range(base_num_blocks + 20, len(new_blocks)):
|
|
226
|
++
await full_node_api.full_node.add_block(new_blocks[i])
|
|
87
227
|
|
|
88
|
--
|
|
89
|
--
|
|
90
|
--
)
|
|
91
|
--
|
|
92
|
--
bh = res_block_headers.header_blocks
|
|
93
|
--
assert len(bh) == 6
|
|
94
|
--
|
|
95
|
--
# @pytest.mark.parametrize(
|
|
96
|
--
# "test_case",
|
|
97
|
--
# [(1000000, 10000010, False, ProtocolMessageTypes.reject_block_headers)],
|
|
98
|
--
# [(80, 99, False, ProtocolMessageTypes.respond_block_headers)],
|
|
99
|
--
# [(10, 8, False, None)],
|
|
100
|
--
# )
|
|
101
|
--
@pytest.mark.anyio
|
|
102
|
--
async def test_request_block_headers_rejected(self, simulator_and_wallet, default_1000_blocks):
|
|
103
|
--
# Tests the edge case of receiving funds right before the recent blocks in weight proof
|
|
104
|
--
[full_node_api], _, bt = simulator_and_wallet
|
|
105
|
--
|
|
106
|
--
# start_height, end_height, return_filter, expected_res = test_case
|
|
107
|
--
|
|
108
|
--
msg = await full_node_api.request_block_headers(
|
|
109
|
--
wallet_protocol.RequestBlockHeaders(uint32(1000000), uint32(1000010), False)
|
|
110
|
--
)
|
|
111
|
--
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
228
|
++
for wallet_node, wallet_server in wallets:
|
|
229
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
230
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
231
|
++
await time_out_assert(30, wallet.get_confirmed_balance, 10 * calculate_pool_reward(uint32(1000)))
|
|
112
232
|
|
|
113
|
--
for block in default_1000_blocks[:150]:
|
|
114
|
--
await full_node_api.full_node.add_block(block)
|
|
115
233
|
|
|
116
|
--
|
|
117
|
--
|
|
118
|
--
|
|
119
|
--
|
|
120
|
--
|
|
121
|
--
wallet_protocol.RequestBlockHeaders(uint32(10), uint32(8), False)
|
|
122
|
--
)
|
|
123
|
--
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
234
|
++
@pytest.mark.anyio
|
|
235
|
++
async def test_backtrack_sync_wallet(two_wallet_nodes, default_400_blocks, self_hostname):
|
|
236
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
237
|
++
full_node_api = full_nodes[0]
|
|
238
|
++
full_node_server = full_node_api.full_node.server
|
|
124
239
|
|
|
125
|
--
|
|
126
|
--
|
|
127
|
--
)
|
|
128
|
--
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
240
|
++
# Trusted node sync
|
|
241
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
129
242
|
|
|
130
|
--
|
|
131
|
--
|
|
132
|
--
wallet_protocol.RequestBlockHeaders(uint32(10), uint32(140), True)
|
|
133
|
--
)
|
|
134
|
--
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
243
|
++
# Untrusted node sync
|
|
244
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
135
245
|
|
|
136
|
--
|
|
137
|
--
|
|
138
|
--
)
|
|
139
|
--
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
140
|
--
msg = await full_node_api.request_block_headers(
|
|
141
|
--
wallet_protocol.RequestBlockHeaders(uint32(90), uint32(160), True)
|
|
142
|
--
)
|
|
143
|
--
assert msg.type == ProtocolMessageTypes.reject_block_headers.value
|
|
246
|
++
for block in default_400_blocks[:20]:
|
|
247
|
++
await full_node_api.full_node.add_block(block)
|
|
144
248
|
|
|
145
|
--
|
|
146
|
--
|
|
147
|
--
[
|
|
148
|
--
dict(
|
|
149
|
--
disable_capabilities=[Capability.BLOCK_HEADERS],
|
|
150
|
--
),
|
|
151
|
--
dict(
|
|
152
|
--
disable_capabilities=[Capability.BASE],
|
|
153
|
--
),
|
|
154
|
--
],
|
|
155
|
--
indirect=True,
|
|
156
|
--
)
|
|
157
|
--
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
158
|
--
@pytest.mark.anyio
|
|
159
|
--
async def test_basic_sync_wallet(self, two_wallet_nodes, default_400_blocks, self_hostname):
|
|
160
|
--
full_nodes, wallets, bt = two_wallet_nodes
|
|
161
|
--
full_node_api = full_nodes[0]
|
|
162
|
--
full_node_server = full_node_api.full_node.server
|
|
249
|
++
for wallet_node, wallet_server in wallets:
|
|
250
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
163
251
|
|
|
164
|
--
|
|
165
|
--
|
|
252
|
++
for wallet_node, wallet_server in wallets:
|
|
253
|
++
await time_out_assert(100, wallet_height_at_least, True, wallet_node, 19)
|
|
166
254
|
|
|
167
|
--
# Untrusted node sync
|
|
168
|
--
wallets[1][0].config["trusted_peers"] = {}
|
|
169
255
|
|
|
170
|
--
|
|
171
|
--
|
|
256
|
++
# Tests a reorg with the wallet
|
|
257
|
++
@pytest.mark.anyio
|
|
258
|
++
async def test_short_batch_sync_wallet(two_wallet_nodes, default_400_blocks, self_hostname):
|
|
259
|
++
full_nodes, wallets, _ = two_wallet_nodes
|
|
260
|
++
full_node_api = full_nodes[0]
|
|
261
|
++
full_node_server = full_node_api.full_node.server
|
|
172
262
|
|
|
173
|
--
|
|
174
|
--
|
|
263
|
++
# Trusted node sync
|
|
264
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
175
265
|
|
|
176
|
--
|
|
177
|
--
|
|
266
|
++
# Untrusted node sync
|
|
267
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
178
268
|
|
|
179
|
--
|
|
180
|
--
|
|
181
|
--
blocks_reorg = bt.get_consecutive_blocks(num_blocks - 1, block_list_input=default_400_blocks[:-5])
|
|
182
|
--
blocks_reorg = bt.get_consecutive_blocks(1, blocks_reorg, guarantee_transaction_block=True, current_time=True)
|
|
183
|
--
for i in range(1, len(blocks_reorg)):
|
|
184
|
--
await full_node_api.full_node.add_block(blocks_reorg[i])
|
|
185
|
--
|
|
186
|
--
for wallet_node, wallet_server in wallets:
|
|
187
|
--
await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
|
|
188
|
--
|
|
189
|
--
for wallet_node, wallet_server in wallets:
|
|
190
|
--
await time_out_assert(
|
|
191
|
--
100, wallet_height_at_least, True, wallet_node, len(default_400_blocks) + num_blocks - 5 - 1
|
|
192
|
--
)
|
|
193
|
--
await time_out_assert(20, wallet_node.wallet_state_manager.synced)
|
|
194
|
--
await disconnect_all(wallet_server)
|
|
195
|
--
assert not (await wallet_node.wallet_state_manager.synced())
|
|
269
|
++
for block in default_400_blocks[:200]:
|
|
270
|
++
await full_node_api.full_node.add_block(block)
|
|
196
271
|
|
|
197
|
--
|
|
198
|
--
|
|
199
|
--
[
|
|
200
|
--
dict(
|
|
201
|
--
disable_capabilities=[Capability.BLOCK_HEADERS],
|
|
202
|
--
),
|
|
203
|
--
dict(
|
|
204
|
--
disable_capabilities=[Capability.BASE],
|
|
205
|
--
),
|
|
206
|
--
],
|
|
207
|
--
indirect=True,
|
|
208
|
--
)
|
|
209
|
--
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
210
|
--
@pytest.mark.anyio
|
|
211
|
--
async def test_almost_recent(self, two_wallet_nodes, default_400_blocks, self_hostname, blockchain_constants):
|
|
212
|
--
# Tests the edge case of receiving funds right before the recent blocks in weight proof
|
|
213
|
--
full_nodes, wallets, bt = two_wallet_nodes
|
|
214
|
--
full_node_api = full_nodes[0]
|
|
215
|
--
full_node_server = full_node_api.full_node.server
|
|
216
|
--
|
|
217
|
--
# Trusted node sync
|
|
218
|
--
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
219
|
--
|
|
220
|
--
# Untrusted node sync
|
|
221
|
--
wallets[1][0].config["trusted_peers"] = {}
|
|
222
|
--
|
|
223
|
--
base_num_blocks = 400
|
|
224
|
--
for block in default_400_blocks:
|
|
225
|
--
await full_node_api.full_node.add_block(block)
|
|
226
|
--
all_blocks = default_400_blocks
|
|
227
|
--
both_phs = []
|
|
228
|
--
for wallet_node, wallet_server in wallets:
|
|
229
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
230
|
--
both_phs.append(await wallet.get_new_puzzlehash())
|
|
231
|
--
|
|
232
|
--
for i in range(20):
|
|
233
|
--
# Tests a reorg with the wallet
|
|
234
|
--
ph = both_phs[i % 2]
|
|
235
|
--
all_blocks = bt.get_consecutive_blocks(1, block_list_input=all_blocks, pool_reward_puzzle_hash=ph)
|
|
236
|
--
await full_node_api.full_node.add_block(all_blocks[-1])
|
|
237
|
--
|
|
238
|
--
new_blocks = bt.get_consecutive_blocks(
|
|
239
|
--
blockchain_constants.WEIGHT_PROOF_RECENT_BLOCKS + 10, block_list_input=all_blocks
|
|
240
|
--
)
|
|
241
|
--
for i in range(base_num_blocks + 20, len(new_blocks)):
|
|
242
|
--
await full_node_api.full_node.add_block(new_blocks[i])
|
|
272
|
++
for wallet_node, wallet_server in wallets:
|
|
273
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
243
274
|
|
|
244
|
--
|
|
245
|
--
|
|
246
|
--
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
247
|
--
await time_out_assert(30, wallet.get_confirmed_balance, 10 * calculate_pool_reward(uint32(1000)))
|
|
275
|
++
for wallet_node, wallet_server in wallets:
|
|
276
|
++
await time_out_assert(100, wallet_height_at_least, True, wallet_node, 199)
|
|
248
277
|
|
|
249
|
--
@pytest.mark.anyio
|
|
250
|
--
async def test_backtrack_sync_wallet(self, two_wallet_nodes, default_400_blocks, self_hostname):
|
|
251
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
252
|
--
full_node_api = full_nodes[0]
|
|
253
|
--
full_node_server = full_node_api.full_node.server
|
|
254
278
|
|
|
255
|
--
|
|
256
|
--
|
|
279
|
++
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
280
|
++
@pytest.mark.anyio
|
|
281
|
++
async def test_long_sync_wallet(two_wallet_nodes, default_1000_blocks, default_400_blocks, self_hostname):
|
|
282
|
++
full_nodes, wallets, bt = two_wallet_nodes
|
|
283
|
++
full_node_api = full_nodes[0]
|
|
284
|
++
full_node_server = full_node_api.full_node.server
|
|
257
285
|
|
|
258
|
--
|
|
259
|
--
|
|
286
|
++
# Trusted node sync
|
|
287
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
260
288
|
|
|
261
|
--
|
|
262
|
--
|
|
289
|
++
# Untrusted node sync
|
|
290
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
263
291
|
|
|
264
|
--
|
|
265
|
--
|
|
292
|
++
for block in default_400_blocks:
|
|
293
|
++
await full_node_api.full_node.add_block(block)
|
|
266
294
|
|
|
267
|
--
|
|
268
|
--
|
|
295
|
++
for wallet_node, wallet_server in wallets:
|
|
296
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
269
297
|
|
|
270
|
--
|
|
271
|
--
|
|
272
|
--
async def test_short_batch_sync_wallet(self, two_wallet_nodes, default_400_blocks, self_hostname):
|
|
273
|
--
full_nodes, wallets, _ = two_wallet_nodes
|
|
274
|
--
full_node_api = full_nodes[0]
|
|
275
|
--
full_node_server = full_node_api.full_node.server
|
|
298
|
++
for wallet_node, wallet_server in wallets:
|
|
299
|
++
await time_out_assert(600, wallet_height_at_least, True, wallet_node, len(default_400_blocks) - 1)
|
|
276
300
|
|
|
277
|
--
|
|
278
|
--
|
|
301
|
++
# Tests a long reorg
|
|
302
|
++
for block in default_1000_blocks:
|
|
303
|
++
await full_node_api.full_node.add_block(block)
|
|
279
304
|
|
|
280
|
--
|
|
281
|
--
|
|
305
|
++
for wallet_node, wallet_server in wallets:
|
|
306
|
++
await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
|
|
282
307
|
|
|
283
|
--
|
|
284
|
--
|
|
308
|
++
log.info(f"wallet node height is {await wallet_node.wallet_state_manager.blockchain.get_finished_sync_up_to()}")
|
|
309
|
++
await time_out_assert(600, wallet_height_at_least, True, wallet_node, len(default_1000_blocks) - 1)
|
|
285
310
|
|
|
286
|
--
|
|
287
|
--
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
311
|
++
await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
|
|
288
312
|
|
|
289
|
--
|
|
290
|
--
|
|
313
|
++
# Tests a short reorg
|
|
314
|
++
num_blocks = 30
|
|
315
|
++
blocks_reorg = bt.get_consecutive_blocks(num_blocks, block_list_input=default_1000_blocks[:-5])
|
|
291
316
|
|
|
292
|
--
|
|
293
|
--
|
|
294
|
--
async def test_long_sync_wallet(self, two_wallet_nodes, default_1000_blocks, default_400_blocks, self_hostname):
|
|
295
|
--
full_nodes, wallets, bt = two_wallet_nodes
|
|
296
|
--
full_node_api = full_nodes[0]
|
|
297
|
--
full_node_server = full_node_api.full_node.server
|
|
317
|
++
for i in range(len(blocks_reorg) - num_blocks - 10, len(blocks_reorg)):
|
|
318
|
++
await full_node_api.full_node.add_block(blocks_reorg[i])
|
|
298
319
|
|
|
299
|
--
|
|
300
|
--
|
|
320
|
++
for wallet_node, wallet_server in wallets:
|
|
321
|
++
await time_out_assert(
|
|
322
|
++
600, wallet_height_at_least, True, wallet_node, len(default_1000_blocks) + num_blocks - 5 - 1
|
|
323
|
++
)
|
|
301
324
|
|
|
302
|
--
# Untrusted node sync
|
|
303
|
--
wallets[1][0].config["trusted_peers"] = {}
|
|
304
325
|
|
|
305
|
--
|
|
306
|
--
|
|
326
|
++
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
327
|
++
@pytest.mark.anyio
|
|
328
|
++
async def test_wallet_reorg_sync(two_wallet_nodes, default_400_blocks, self_hostname):
|
|
329
|
++
num_blocks = 5
|
|
330
|
++
full_nodes, wallets, bt = two_wallet_nodes
|
|
331
|
++
full_node_api = full_nodes[0]
|
|
332
|
++
full_node_server = full_node_api.full_node.server
|
|
307
333
|
|
|
308
|
--
|
|
309
|
--
|
|
334
|
++
# Trusted node sync
|
|
335
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
310
336
|
|
|
311
|
--
|
|
312
|
--
|
|
337
|
++
# Untrusted node sync
|
|
338
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
313
339
|
|
|
314
|
--
|
|
315
|
--
|
|
316
|
--
|
|
340
|
++
phs = []
|
|
341
|
++
for wallet_node, wallet_server in wallets:
|
|
342
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
343
|
++
phs.append(await wallet.get_new_puzzlehash())
|
|
344
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
317
345
|
|
|
318
|
--
|
|
319
|
--
|
|
346
|
++
# Insert 400 blocks
|
|
347
|
++
for block in default_400_blocks:
|
|
348
|
++
await full_node_api.full_node.add_block(block)
|
|
320
349
|
|
|
321
|
--
|
|
322
|
--
|
|
323
|
--
|
|
324
|
--
await time_out_assert(600, wallet_height_at_least, True, wallet_node, len(default_1000_blocks) - 1)
|
|
350
|
++
# Farm few more with reward
|
|
351
|
++
for i in range(0, num_blocks - 1):
|
|
352
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(phs[0]))
|
|
325
353
|
|
|
326
|
--
|
|
354
|
++
for i in range(0, num_blocks):
|
|
355
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(phs[1]))
|
|
327
356
|
|
|
328
|
--
|
|
329
|
--
|
|
330
|
--
|
|
357
|
++
# Confirm we have the funds
|
|
358
|
++
funds = sum(
|
|
359
|
++
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
|
|
360
|
++
)
|
|
331
361
|
|
|
332
|
--
|
|
333
|
--
|
|
362
|
++
for wallet_node, wallet_server in wallets:
|
|
363
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
364
|
++
await time_out_assert(60, wallet.get_confirmed_balance, funds)
|
|
365
|
++
await time_out_assert(60, get_tx_count, 2 * (num_blocks - 1), wallet_node.wallet_state_manager, 1)
|
|
334
366
|
|
|
335
|
--
|
|
336
|
--
|
|
337
|
--
|
|
338
|
--
)
|
|
367
|
++
# Reorg blocks that carry reward
|
|
368
|
++
num_blocks = 30
|
|
369
|
++
blocks_reorg = bt.get_consecutive_blocks(num_blocks, block_list_input=default_400_blocks[:-5])
|
|
339
370
|
|
|
340
|
--
|
|
341
|
--
|
|
342
|
--
async def test_wallet_reorg_sync(self, two_wallet_nodes, default_400_blocks, self_hostname):
|
|
343
|
--
num_blocks = 5
|
|
344
|
--
full_nodes, wallets, bt = two_wallet_nodes
|
|
345
|
--
full_node_api = full_nodes[0]
|
|
346
|
--
full_node_server = full_node_api.full_node.server
|
|
371
|
++
for block in blocks_reorg[-30:]:
|
|
372
|
++
await full_node_api.full_node.add_block(block)
|
|
347
373
|
|
|
348
|
--
|
|
349
|
--
|
|
374
|
++
for wallet_node, wallet_server in wallets:
|
|
375
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
376
|
++
await time_out_assert(60, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
|
|
377
|
++
await time_out_assert(60, wallet.get_confirmed_balance, 0)
|
|
350
378
|
|
|
351
|
--
# Untrusted node sync
|
|
352
|
--
wallets[1][0].config["trusted_peers"] = {}
|
|
353
379
|
|
|
354
|
--
|
|
355
|
--
|
|
356
|
--
|
|
357
|
--
|
|
358
|
--
|
|
380
|
++
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
381
|
++
@pytest.mark.anyio
|
|
382
|
++
async def test_wallet_reorg_get_coinbase(two_wallet_nodes, default_400_blocks, self_hostname):
|
|
383
|
++
full_nodes, wallets, bt = two_wallet_nodes
|
|
384
|
++
full_node_api = full_nodes[0]
|
|
385
|
++
full_node_server = full_node_api.full_node.server
|
|
359
386
|
|
|
360
|
--
|
|
361
|
--
|
|
362
|
--
await full_node_api.full_node.add_block(block)
|
|
387
|
++
# Trusted node sync
|
|
388
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
363
389
|
|
|
364
|
--
|
|
365
|
--
|
|
366
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(phs[0]))
|
|
390
|
++
# Untrusted node sync
|
|
391
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
367
392
|
|
|
368
|
--
|
|
369
|
--
|
|
393
|
++
for wallet_node, wallet_server in wallets:
|
|
394
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
370
395
|
|
|
371
|
--
|
|
372
|
--
|
|
373
|
--
|
|
374
|
--
)
|
|
396
|
++
# Insert 400 blocks
|
|
397
|
++
for block in default_400_blocks:
|
|
398
|
++
await full_node_api.full_node.add_block(block)
|
|
375
399
|
|
|
376
|
--
|
|
377
|
--
|
|
378
|
--
|
|
379
|
--
|
|
380
|
--
for wallet_node, wallet_server in wallets:
|
|
381
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
382
|
--
await time_out_assert(60, wallet.get_confirmed_balance, funds)
|
|
383
|
--
await time_out_assert(60, get_tx_count, 2 * (num_blocks - 1), wallet_node.wallet_state_manager, 1)
|
|
384
|
--
|
|
385
|
--
# Reorg blocks that carry reward
|
|
386
|
--
num_blocks = 30
|
|
387
|
--
blocks_reorg = bt.get_consecutive_blocks(num_blocks, block_list_input=default_400_blocks[:-5])
|
|
388
|
--
|
|
389
|
--
for block in blocks_reorg[-30:]:
|
|
390
|
--
await full_node_api.full_node.add_block(block)
|
|
391
|
--
|
|
392
|
--
for wallet_node, wallet_server in wallets:
|
|
393
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
394
|
--
await time_out_assert(60, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
|
|
395
|
--
await time_out_assert(60, wallet.get_confirmed_balance, 0)
|
|
396
|
--
|
|
397
|
--
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
398
|
--
@pytest.mark.anyio
|
|
399
|
--
async def test_wallet_reorg_get_coinbase(self, two_wallet_nodes, default_400_blocks, self_hostname):
|
|
400
|
--
full_nodes, wallets, bt = two_wallet_nodes
|
|
401
|
--
full_node_api = full_nodes[0]
|
|
402
|
--
full_node_server = full_node_api.full_node.server
|
|
403
|
--
|
|
404
|
--
# Trusted node sync
|
|
405
|
--
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
406
|
--
|
|
407
|
--
# Untrusted node sync
|
|
408
|
--
wallets[1][0].config["trusted_peers"] = {}
|
|
409
|
--
|
|
410
|
--
for wallet_node, wallet_server in wallets:
|
|
411
|
--
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
412
|
--
|
|
413
|
--
# Insert 400 blocks
|
|
414
|
--
for block in default_400_blocks:
|
|
415
|
--
await full_node_api.full_node.add_block(block)
|
|
416
|
--
|
|
417
|
--
# Reorg blocks that carry reward
|
|
418
|
--
num_blocks_reorg = 30
|
|
419
|
--
blocks_reorg = bt.get_consecutive_blocks(num_blocks_reorg, block_list_input=default_400_blocks[:-5])
|
|
420
|
--
|
|
421
|
--
for block in blocks_reorg[:-5]:
|
|
422
|
--
await full_node_api.full_node.add_block(block)
|
|
423
|
--
|
|
424
|
--
async def get_tx_count(wsm, wallet_id):
|
|
425
|
--
txs = await wsm.get_all_transactions(wallet_id)
|
|
426
|
--
return len(txs)
|
|
427
|
--
|
|
428
|
--
for wallet_node, wallet_server in wallets:
|
|
429
|
--
await time_out_assert(30, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
|
|
430
|
--
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=30)
|
|
431
|
--
|
|
432
|
--
num_blocks_reorg_1 = 40
|
|
433
|
--
all_blocks_reorg_2 = blocks_reorg[:-30]
|
|
434
|
--
for wallet_node, wallet_server in wallets:
|
|
435
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
436
|
--
ph = await wallet.get_new_puzzlehash()
|
|
437
|
--
all_blocks_reorg_2 = bt.get_consecutive_blocks(
|
|
438
|
--
1, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph, block_list_input=all_blocks_reorg_2
|
|
439
|
--
)
|
|
440
|
--
blocks_reorg_2 = bt.get_consecutive_blocks(num_blocks_reorg_1, block_list_input=all_blocks_reorg_2)
|
|
400
|
++
# Reorg blocks that carry reward
|
|
401
|
++
num_blocks_reorg = 30
|
|
402
|
++
blocks_reorg = bt.get_consecutive_blocks(num_blocks_reorg, block_list_input=default_400_blocks[:-5])
|
|
441
403
|
|
|
442
|
--
|
|
443
|
--
|
|
404
|
++
for block in blocks_reorg[:-5]:
|
|
405
|
++
await full_node_api.full_node.add_block(block)
|
|
444
406
|
|
|
445
|
--
|
|
446
|
--
|
|
407
|
++
for wallet_node, wallet_server in wallets:
|
|
408
|
++
await time_out_assert(30, get_tx_count, 0, wallet_node.wallet_state_manager, 1)
|
|
409
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=30)
|
|
447
410
|
|
|
448
|
--
|
|
449
|
--
|
|
450
|
--
|
|
411
|
++
num_blocks_reorg_1 = 40
|
|
412
|
++
all_blocks_reorg_2 = blocks_reorg[:-30]
|
|
413
|
++
for wallet_node, wallet_server in wallets:
|
|
414
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
415
|
++
ph = await wallet.get_new_puzzlehash()
|
|
416
|
++
all_blocks_reorg_2 = bt.get_consecutive_blocks(
|
|
417
|
++
1, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph, block_list_input=all_blocks_reorg_2
|
|
451
418
|
)
|
|
419
|
++
blocks_reorg_2 = bt.get_consecutive_blocks(num_blocks_reorg_1, block_list_input=all_blocks_reorg_2)
|
|
452
420
|
|
|
453
|
--
|
|
454
|
--
|
|
455
|
--
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=60)
|
|
421
|
++
for block in blocks_reorg_2[-44:]:
|
|
422
|
++
await full_node_api.full_node.add_block(block)
|
|
456
423
|
|
|
457
|
--
|
|
458
|
--
|
|
424
|
++
for wallet_node, wallet_server in wallets:
|
|
425
|
++
await disconnect_all_and_reconnect(wallet_server, full_node_server, self_hostname)
|
|
459
426
|
|
|
460
|
--
|
|
461
|
--
|
|
462
|
--
|
|
463
|
--
|
|
427
|
++
# Confirm we have the funds
|
|
428
|
++
funds = calculate_pool_reward(uint32(len(all_blocks_reorg_2))) + calculate_base_farmer_reward(
|
|
429
|
++
uint32(len(all_blocks_reorg_2))
|
|
430
|
++
)
|
|
431
|
++
|
|
432
|
++
for wallet_node, wallet_server in wallets:
|
|
464
433
|
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
465
|
--
|
|
434
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=60)
|
|
466
435
|
|
|
467
|
--
|
|
468
|
--
await
|
|
436
|
++
await time_out_assert(20, get_tx_count, 2, wallet_node.wallet_state_manager, 1)
|
|
437
|
++
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
|
469
438
|
|
|
470
|
--
for i in range(2):
|
|
471
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
472
439
|
|
|
473
|
--
|
|
440
|
++
@pytest.mark.anyio
|
|
441
|
++
async def test_request_additions_errors(simulator_and_wallet, self_hostname):
|
|
442
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
443
|
++
wallet_node, wallet_server = wallets[0]
|
|
444
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
445
|
++
ph = await wallet.get_new_puzzlehash()
|
|
474
446
|
|
|
475
|
--
|
|
476
|
--
|
|
447
|
++
full_node_api = full_nodes[0]
|
|
448
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
|
|
477
449
|
|
|
478
|
--
|
|
479
|
--
|
|
480
|
--
await full_node_api.request_additions(RequestAdditions(uint64(100), last_block.header_hash, [ph]))
|
|
450
|
++
for i in range(2):
|
|
451
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
481
452
|
|
|
482
|
--
|
|
483
|
--
with pytest.raises(ValueError):
|
|
484
|
--
await full_node_api.request_additions(RequestAdditions(last_block.height, std_hash(b""), [ph]))
|
|
453
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
485
454
|
|
|
486
|
--
|
|
487
|
--
|
|
488
|
--
RequestAdditions(last_block.height, last_block.header_hash, [std_hash(b"")])
|
|
489
|
--
)
|
|
490
|
--
assert res1 is not None
|
|
491
|
--
response = RespondAdditions.from_bytes(res1.data)
|
|
492
|
--
assert response.height == last_block.height
|
|
493
|
--
assert response.header_hash == last_block.header_hash
|
|
494
|
--
assert len(response.proofs) == 1
|
|
495
|
--
assert len(response.coins) == 1
|
|
496
|
--
|
|
497
|
--
assert response.proofs[0][0] == std_hash(b"")
|
|
498
|
--
assert response.proofs[0][1] is not None
|
|
499
|
--
assert response.proofs[0][2] is None
|
|
500
|
--
|
|
501
|
--
@pytest.mark.anyio
|
|
502
|
--
async def test_request_additions_success(self, simulator_and_wallet, self_hostname):
|
|
503
|
--
full_nodes, wallets, _ = simulator_and_wallet
|
|
504
|
--
wallet_node, wallet_server = wallets[0]
|
|
505
|
--
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
506
|
--
ph = await wallet.get_new_puzzlehash()
|
|
455
|
++
last_block: Optional[BlockRecord] = full_node_api.full_node.blockchain.get_peak()
|
|
456
|
++
assert last_block is not None
|
|
507
457
|
|
|
508
|
--
|
|
509
|
--
|
|
458
|
++
# Invalid height
|
|
459
|
++
with pytest.raises(ValueError):
|
|
460
|
++
await full_node_api.request_additions(RequestAdditions(uint64(100), last_block.header_hash, [ph]))
|
|
510
461
|
|
|
511
|
--
|
|
512
|
--
|
|
462
|
++
# Invalid header hash
|
|
463
|
++
with pytest.raises(ValueError):
|
|
464
|
++
await full_node_api.request_additions(RequestAdditions(last_block.height, std_hash(b""), [ph]))
|
|
513
465
|
|
|
514
|
--
|
|
466
|
++
# No results
|
|
467
|
++
res1: Optional[Message] = await full_node_api.request_additions(
|
|
468
|
++
RequestAdditions(last_block.height, last_block.header_hash, [std_hash(b"")])
|
|
469
|
++
)
|
|
470
|
++
assert res1 is not None
|
|
471
|
++
response = RespondAdditions.from_bytes(res1.data)
|
|
472
|
++
assert response.height == last_block.height
|
|
473
|
++
assert response.header_hash == last_block.header_hash
|
|
474
|
++
assert len(response.proofs) == 1
|
|
475
|
++
assert len(response.coins) == 1
|
|
515
476
|
|
|
516
|
--
|
|
517
|
--
|
|
518
|
--
|
|
519
|
--
payees.append(Payment(payee_ph, uint64(i + 100)))
|
|
520
|
--
payees.append(Payment(payee_ph, uint64(i + 200)))
|
|
477
|
++
assert response.proofs[0][0] == std_hash(b"")
|
|
478
|
++
assert response.proofs[0][1] is not None
|
|
479
|
++
assert response.proofs[0][2] is None
|
|
521
480
|
|
|
522
|
--
[tx] = await wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
523
|
--
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
524
481
|
|
|
525
|
--
|
|
482
|
++
@pytest.mark.anyio
|
|
483
|
++
async def test_request_additions_success(simulator_and_wallet, self_hostname):
|
|
484
|
++
full_nodes, wallets, _ = simulator_and_wallet
|
|
485
|
++
wallet_node, wallet_server = wallets[0]
|
|
486
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
487
|
++
ph = await wallet.get_new_puzzlehash()
|
|
526
488
|
|
|
527
|
--
|
|
528
|
--
|
|
529
|
--
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
489
|
++
full_node_api = full_nodes[0]
|
|
490
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
|
|
530
491
|
|
|
531
|
--
|
|
532
|
--
|
|
533
|
--
last_block.height,
|
|
534
|
--
None,
|
|
535
|
--
[payees[0].puzzle_hash, payees[2].puzzle_hash, std_hash(b"1")],
|
|
536
|
--
)
|
|
537
|
--
)
|
|
492
|
++
for i in range(2):
|
|
493
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
538
494
|
|
|
539
|
--
|
|
540
|
--
response = RespondAdditions.from_bytes(res2.data)
|
|
541
|
--
assert response.height == last_block.height
|
|
542
|
--
assert response.header_hash == last_block.header_hash
|
|
543
|
--
assert len(response.proofs) == 3
|
|
495
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
544
496
|
|
|
545
|
--
|
|
546
|
--
|
|
547
|
--
|
|
548
|
--
|
|
549
|
--
|
|
497
|
++
payees: List[Payment] = []
|
|
498
|
++
for i in range(10):
|
|
499
|
++
payee_ph = await wallet.get_new_puzzlehash()
|
|
500
|
++
payees.append(Payment(payee_ph, uint64(i + 100)))
|
|
501
|
++
payees.append(Payment(payee_ph, uint64(i + 200)))
|
|
550
502
|
|
|
551
|
--
|
|
552
|
--
|
|
503
|
++
[tx] = await wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
504
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
553
505
|
|
|
554
|
--
|
|
506
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
555
507
|
|
|
556
|
--
|
|
557
|
--
|
|
558
|
--
|
|
559
|
--
# this is the one that is not included
|
|
560
|
--
assert len(coin_list) == 0
|
|
561
|
--
else:
|
|
562
|
--
for coin in coin_list:
|
|
563
|
--
assert coin.puzzle_hash == p
|
|
564
|
--
# The other ones are included
|
|
565
|
--
assert len(coin_list) == 2
|
|
566
|
--
|
|
567
|
--
# None for puzzle hashes returns all coins and no proofs
|
|
568
|
--
res3: Optional[Message] = await full_node_api.request_additions(
|
|
569
|
--
RequestAdditions(last_block.height, last_block.header_hash, None)
|
|
570
|
--
)
|
|
508
|
++
last_block: Optional[BlockRecord] = full_node_api.full_node.blockchain.get_peak()
|
|
509
|
++
assert last_block is not None
|
|
510
|
++
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
|
|
571
511
|
|
|
572
|
--
|
|
573
|
--
|
|
574
|
--
|
|
575
|
--
|
|
576
|
--
|
|
577
|
--
assert len(response.coins) == 12
|
|
578
|
--
assert sum([len(c_list) for _, c_list in response.coins]) == 24
|
|
579
|
--
|
|
580
|
--
# [] for puzzle hashes returns nothing
|
|
581
|
--
res4: Optional[Message] = await full_node_api.request_additions(
|
|
582
|
--
RequestAdditions(last_block.height, last_block.header_hash, [])
|
|
583
|
--
)
|
|
584
|
--
assert res4 is not None
|
|
585
|
--
response = RespondAdditions.from_bytes(res4.data)
|
|
586
|
--
assert response.proofs == []
|
|
587
|
--
assert len(response.coins) == 0
|
|
588
|
--
|
|
589
|
--
@pytest.mark.anyio
|
|
590
|
--
async def test_get_wp_fork_point(self, default_10000_blocks, blockchain_constants):
|
|
591
|
--
blocks = default_10000_blocks
|
|
592
|
--
header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
|
|
593
|
--
blocks, blockchain_constants
|
|
512
|
++
res2: Optional[Message] = await full_node_api.request_additions(
|
|
513
|
++
RequestAdditions(
|
|
514
|
++
last_block.height,
|
|
515
|
++
None,
|
|
516
|
++
[payees[0].puzzle_hash, payees[2].puzzle_hash, std_hash(b"1")],
|
|
594
517
|
)
|
|
595
|
--
|
|
596
|
--
|
|
597
|
--
|
|
598
|
--
|
|
599
|
--
|
|
600
|
--
|
|
601
|
--
|
|
602
|
--
|
|
603
|
--
|
|
604
|
--
|
|
605
|
--
|
|
606
|
--
|
|
607
|
--
|
|
608
|
--
|
|
609
|
--
|
|
610
|
--
|
|
611
|
--
# overlap between recent chain in wps, fork point is the tip of the shorter wp
|
|
612
|
--
assert fork12 == wp1.recent_chain_data[-1].height
|
|
613
|
--
assert fork16 == wp1.recent_chain_data[-1].height
|
|
614
|
--
|
|
615
|
--
# if there is an overlap between the recent chains we can find the exact fork point
|
|
616
|
--
# if not we should get the latest block with a sub epoch summary that exists in both wp's
|
|
617
|
--
# this can happen in fork24 and fork14 since they are not very far and also not very close
|
|
618
|
--
|
|
619
|
--
if wp2.recent_chain_data[0].height > wp4.recent_chain_data[-1].height:
|
|
620
|
--
assert fork24 in summaries.keys()
|
|
621
|
--
assert fork24 < wp4.recent_chain_data[-1].height
|
|
622
|
--
else:
|
|
623
|
--
assert fork24 == wp4.recent_chain_data[-1].height
|
|
518
|
++
)
|
|
519
|
++
|
|
520
|
++
assert res2 is not None
|
|
521
|
++
response = RespondAdditions.from_bytes(res2.data)
|
|
522
|
++
assert response.height == last_block.height
|
|
523
|
++
assert response.header_hash == last_block.header_hash
|
|
524
|
++
assert len(response.proofs) == 3
|
|
525
|
++
|
|
526
|
++
# First two PHs are included
|
|
527
|
++
for i in range(2):
|
|
528
|
++
assert response.proofs[i][0] in {payees[j].puzzle_hash for j in (0, 2)}
|
|
529
|
++
assert response.proofs[i][1] is not None
|
|
530
|
++
assert response.proofs[i][2] is not None
|
|
531
|
++
|
|
532
|
++
# Third PH is not included
|
|
533
|
++
assert response.proofs[2][2] is None
|
|
624
534
|
|
|
625
|
--
|
|
626
|
--
|
|
627
|
--
|
|
535
|
++
coin_list_dict = {p: coin_list for p, coin_list in response.coins}
|
|
536
|
++
|
|
537
|
++
assert len(coin_list_dict) == 3
|
|
538
|
++
for p, coin_list in coin_list_dict.items():
|
|
539
|
++
if p == std_hash(b"1"):
|
|
540
|
++
# this is the one that is not included
|
|
541
|
++
assert len(coin_list) == 0
|
|
628
542
|
else:
|
|
629
|
--
|
|
630
|
--
|
|
631
|
--
|
|
632
|
--
|
|
633
|
--
|
|
634
|
--
|
|
635
|
--
|
|
636
|
--
|
|
637
|
--
assert fork34 in summaries.keys()
|
|
638
|
--
assert fork23 < wp3.recent_chain_data[-1].height
|
|
639
|
--
assert fork45 in summaries.keys()
|
|
640
|
--
assert fork45 < wp4.recent_chain_data[-1].height
|
|
641
|
--
|
|
642
|
--
"""
|
|
643
|
--
This tests that a wallet filters out the dust properly.
|
|
644
|
--
It runs in seven phases:
|
|
645
|
--
1. Create a single dust coin.
|
|
646
|
--
Typically (though there are edge cases), this coin will not be filtered.
|
|
647
|
--
2. Create dust coins until the filter threshold has been reached.
|
|
648
|
--
At this point, none of the dust should be filtered.
|
|
649
|
--
3. Create 10 coins that are exactly the size of the filter threshold.
|
|
650
|
--
These should not be filtered because they are not dust.
|
|
651
|
--
4. Create one more dust coin. This coin should be filtered.
|
|
652
|
--
5. Create 5 coins below the threshold and 5 at or above.
|
|
653
|
--
Those below the threshold should get filtered, and those above should not.
|
|
654
|
--
6. Clear all coins from the dust wallet.
|
|
655
|
--
Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
|
|
656
|
--
Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
|
|
657
|
--
7: Create an NFT wallet for the farmer wallet, and generate an NFT in that wallet.
|
|
658
|
--
Create an NFT wallet for the dust wallet.
|
|
659
|
--
Send the NFT to the dust wallet. The NFT should not be filtered.
|
|
660
|
--
"""
|
|
661
|
--
|
|
662
|
--
@pytest.mark.anyio
|
|
663
|
--
@pytest.mark.parametrize(
|
|
664
|
--
"spam_filter_after_n_txs, xch_spam_amount, dust_value",
|
|
665
|
--
[
|
|
666
|
--
# In the following tests, the filter is run right away:
|
|
667
|
--
(0, 1, 1), # nothing is filtered
|
|
668
|
--
# In the following tests, 1 coin will be created in part 1, and 9 in part 2:
|
|
669
|
--
(10, 10000000000, 1), # everything is dust
|
|
670
|
--
(10, 10000000000, 10000000000), # max dust threshold, dust is same size so not filtered
|
|
671
|
--
# Test with more coins
|
|
672
|
--
(105, 1000000, 1), # default filter level (1m mojos), default dust size (1)
|
|
673
|
--
],
|
|
543
|
++
for coin in coin_list:
|
|
544
|
++
assert coin.puzzle_hash == p
|
|
545
|
++
# The other ones are included
|
|
546
|
++
assert len(coin_list) == 2
|
|
547
|
++
|
|
548
|
++
# None for puzzle hashes returns all coins and no proofs
|
|
549
|
++
res3: Optional[Message] = await full_node_api.request_additions(
|
|
550
|
++
RequestAdditions(last_block.height, last_block.header_hash, None)
|
|
674
551
|
)
|
|
675
|
--
async def test_dusted_wallet(
|
|
676
|
--
self,
|
|
677
|
--
self_hostname,
|
|
678
|
--
two_wallet_nodes_custom_spam_filtering,
|
|
679
|
--
spam_filter_after_n_txs,
|
|
680
|
--
xch_spam_amount,
|
|
681
|
--
dust_value,
|
|
682
|
--
):
|
|
683
|
--
full_nodes, wallets, _ = two_wallet_nodes_custom_spam_filtering
|
|
684
|
--
|
|
685
|
--
farm_wallet_node, farm_wallet_server = wallets[0]
|
|
686
|
--
dust_wallet_node, dust_wallet_server = wallets[1]
|
|
687
|
--
|
|
688
|
--
# Create two wallets, one for farming (not used for testing), and one for testing dust.
|
|
689
|
--
farm_wallet = farm_wallet_node.wallet_state_manager.main_wallet
|
|
690
|
--
dust_wallet = dust_wallet_node.wallet_state_manager.main_wallet
|
|
691
|
--
ph = await farm_wallet.get_new_puzzlehash()
|
|
692
|
--
|
|
693
|
--
full_node_api = full_nodes[0]
|
|
694
|
--
|
|
695
|
--
# It's also possible to obtain the current settings for spam_filter_after_n_txs and xch_spam_amount
|
|
696
|
--
# spam_filter_after_n_txs = wallets[0][0].config["spam_filter_after_n_txs"]
|
|
697
|
--
# xch_spam_amount = wallets[0][0].config["xch_spam_amount"]
|
|
698
|
--
# dust_value=1
|
|
699
|
--
|
|
700
|
--
# Verify legal values for the settings to be tested
|
|
701
|
--
# If spam_filter_after_n_txs is greater than 250, this test will take a long time to run.
|
|
702
|
--
# Current max value for xch_spam_amount is 0.01 XCH.
|
|
703
|
--
# If needed, this could be increased but we would need to farm more blocks.
|
|
704
|
--
# The max dust_value could be increased, but would require farming more blocks.
|
|
705
|
--
assert spam_filter_after_n_txs >= 0
|
|
706
|
--
assert spam_filter_after_n_txs <= 250
|
|
707
|
--
assert xch_spam_amount >= 1
|
|
708
|
--
assert xch_spam_amount <= 10000000000
|
|
709
|
--
assert dust_value >= 1
|
|
710
|
--
assert dust_value <= 10000000000
|
|
711
|
--
|
|
712
|
--
# start both clients
|
|
713
|
--
await farm_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
|
|
714
|
--
await dust_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
|
|
715
|
--
|
|
716
|
--
# Farm two blocks
|
|
717
|
--
for i in range(2):
|
|
718
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
719
552
|
|
|
720
|
--
|
|
721
|
--
|
|
553
|
++
assert res3 is not None
|
|
554
|
++
response = RespondAdditions.from_bytes(res3.data)
|
|
555
|
++
assert response.height == last_block.height
|
|
556
|
++
assert response.header_hash == last_block.header_hash
|
|
557
|
++
assert response.proofs is None
|
|
558
|
++
assert len(response.coins) == 12
|
|
559
|
++
assert sum([len(c_list) for _, c_list in response.coins]) == 24
|
|
560
|
++
|
|
561
|
++
# [] for puzzle hashes returns nothing
|
|
562
|
++
res4: Optional[Message] = await full_node_api.request_additions(
|
|
563
|
++
RequestAdditions(last_block.height, last_block.header_hash, [])
|
|
564
|
++
)
|
|
565
|
++
assert res4 is not None
|
|
566
|
++
response = RespondAdditions.from_bytes(res4.data)
|
|
567
|
++
assert response.proofs == []
|
|
568
|
++
assert len(response.coins) == 0
|
|
569
|
++
|
|
570
|
++
|
|
571
|
++
@pytest.mark.anyio
|
|
572
|
++
async def test_get_wp_fork_point(default_10000_blocks, blockchain_constants):
|
|
573
|
++
blocks = default_10000_blocks
|
|
574
|
++
header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks, blockchain_constants)
|
|
575
|
++
wpf = WeightProofHandler(blockchain_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
|
|
576
|
++
wp1 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9000)]].header_hash)
|
|
577
|
++
wp2 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9030)]].header_hash)
|
|
578
|
++
wp3 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(7500)]].header_hash)
|
|
579
|
++
wp4 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(8700)]].header_hash)
|
|
580
|
++
wp5 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9700)]].header_hash)
|
|
581
|
++
wp6 = await wpf.get_proof_of_weight(header_cache[height_to_hash[uint32(9010)]].header_hash)
|
|
582
|
++
fork12 = get_wp_fork_point(blockchain_constants, wp1, wp2)
|
|
583
|
++
fork13 = get_wp_fork_point(blockchain_constants, wp3, wp1)
|
|
584
|
++
fork14 = get_wp_fork_point(blockchain_constants, wp4, wp1)
|
|
585
|
++
fork23 = get_wp_fork_point(blockchain_constants, wp3, wp2)
|
|
586
|
++
fork24 = get_wp_fork_point(blockchain_constants, wp4, wp2)
|
|
587
|
++
fork34 = get_wp_fork_point(blockchain_constants, wp3, wp4)
|
|
588
|
++
fork45 = get_wp_fork_point(blockchain_constants, wp4, wp5)
|
|
589
|
++
fork16 = get_wp_fork_point(blockchain_constants, wp1, wp6)
|
|
590
|
++
|
|
591
|
++
# overlap between recent chain in wps, fork point is the tip of the shorter wp
|
|
592
|
++
assert fork12 == wp1.recent_chain_data[-1].height
|
|
593
|
++
assert fork16 == wp1.recent_chain_data[-1].height
|
|
594
|
++
|
|
595
|
++
# if there is an overlap between the recent chains we can find the exact fork point
|
|
596
|
++
# if not we should get the latest block with a sub epoch summary that exists in both wp's
|
|
597
|
++
# this can happen in fork24 and fork14 since they are not very far and also not very close
|
|
598
|
++
|
|
599
|
++
if wp2.recent_chain_data[0].height > wp4.recent_chain_data[-1].height:
|
|
600
|
++
assert fork24 in summaries.keys()
|
|
601
|
++
assert fork24 < wp4.recent_chain_data[-1].height
|
|
602
|
++
else:
|
|
603
|
++
assert fork24 == wp4.recent_chain_data[-1].height
|
|
604
|
++
|
|
605
|
++
if wp1.recent_chain_data[0].height > wp4.recent_chain_data[-1].height:
|
|
606
|
++
assert fork14 in summaries.keys()
|
|
607
|
++
assert fork14 < wp4.recent_chain_data[-1].height
|
|
608
|
++
else:
|
|
609
|
++
assert fork14 == wp4.recent_chain_data[-1].height
|
|
610
|
++
|
|
611
|
++
# no overlap between recent chain in wps, fork point
|
|
612
|
++
# is the latest block with a sub epoch summary that exists in both wp's
|
|
613
|
++
assert fork13 in summaries.keys()
|
|
614
|
++
assert fork13 < wp3.recent_chain_data[-1].height
|
|
615
|
++
assert fork23 in summaries.keys()
|
|
616
|
++
assert fork23 < wp3.recent_chain_data[-1].height
|
|
617
|
++
assert fork34 in summaries.keys()
|
|
618
|
++
assert fork23 < wp3.recent_chain_data[-1].height
|
|
619
|
++
assert fork45 in summaries.keys()
|
|
620
|
++
assert fork45 < wp4.recent_chain_data[-1].height
|
|
621
|
++
|
|
622
|
++
|
|
623
|
++
"""
|
|
624
|
++
This tests that a wallet filters out the dust properly.
|
|
625
|
++
It runs in seven phases:
|
|
626
|
++
1. Create a single dust coin.
|
|
627
|
++
Typically (though there are edge cases), this coin will not be filtered.
|
|
628
|
++
2. Create dust coins until the filter threshold has been reached.
|
|
629
|
++
At this point, none of the dust should be filtered.
|
|
630
|
++
3. Create 10 coins that are exactly the size of the filter threshold.
|
|
631
|
++
These should not be filtered because they are not dust.
|
|
632
|
++
4. Create one more dust coin. This coin should be filtered.
|
|
633
|
++
5. Create 5 coins below the threshold and 5 at or above.
|
|
634
|
++
Those below the threshold should get filtered, and those above should not.
|
|
635
|
++
6. Clear all coins from the dust wallet.
|
|
636
|
++
Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
|
|
637
|
++
Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
|
|
638
|
++
7: Create an NFT wallet for the farmer wallet, and generate an NFT in that wallet.
|
|
639
|
++
Create an NFT wallet for the dust wallet.
|
|
640
|
++
Send the NFT to the dust wallet. The NFT should not be filtered.
|
|
641
|
++
"""
|
|
642
|
++
|
|
643
|
++
|
|
644
|
++
@pytest.mark.anyio
|
|
645
|
++
@pytest.mark.parametrize(
|
|
646
|
++
"spam_filter_after_n_txs, xch_spam_amount, dust_value",
|
|
647
|
++
[
|
|
648
|
++
# In the following tests, the filter is run right away:
|
|
649
|
++
(0, 1, 1), # nothing is filtered
|
|
650
|
++
# In the following tests, 1 coin will be created in part 1, and 9 in part 2:
|
|
651
|
++
(10, 10000000000, 1), # everything is dust
|
|
652
|
++
(10, 10000000000, 10000000000), # max dust threshold, dust is same size so not filtered
|
|
653
|
++
# Test with more coins
|
|
654
|
++
(105, 1000000, 1), # default filter level (1m mojos), default dust size (1)
|
|
655
|
++
],
|
|
656
|
++
)
|
|
657
|
++
async def test_dusted_wallet(
|
|
658
|
++
self_hostname, two_wallet_nodes_custom_spam_filtering, spam_filter_after_n_txs, xch_spam_amount, dust_value
|
|
659
|
++
):
|
|
660
|
++
full_nodes, wallets, _ = two_wallet_nodes_custom_spam_filtering
|
|
661
|
++
|
|
662
|
++
farm_wallet_node, farm_wallet_server = wallets[0]
|
|
663
|
++
dust_wallet_node, dust_wallet_server = wallets[1]
|
|
664
|
++
|
|
665
|
++
# Create two wallets, one for farming (not used for testing), and one for testing dust.
|
|
666
|
++
farm_wallet = farm_wallet_node.wallet_state_manager.main_wallet
|
|
667
|
++
dust_wallet = dust_wallet_node.wallet_state_manager.main_wallet
|
|
668
|
++
ph = await farm_wallet.get_new_puzzlehash()
|
|
669
|
++
|
|
670
|
++
full_node_api = full_nodes[0]
|
|
671
|
++
|
|
672
|
++
# It's also possible to obtain the current settings for spam_filter_after_n_txs and xch_spam_amount
|
|
673
|
++
# spam_filter_after_n_txs = wallets[0][0].config["spam_filter_after_n_txs"]
|
|
674
|
++
# xch_spam_amount = wallets[0][0].config["xch_spam_amount"]
|
|
675
|
++
# dust_value=1
|
|
676
|
++
|
|
677
|
++
# Verify legal values for the settings to be tested
|
|
678
|
++
# If spam_filter_after_n_txs is greater than 250, this test will take a long time to run.
|
|
679
|
++
# Current max value for xch_spam_amount is 0.01 XCH.
|
|
680
|
++
# If needed, this could be increased but we would need to farm more blocks.
|
|
681
|
++
# The max dust_value could be increased, but would require farming more blocks.
|
|
682
|
++
assert spam_filter_after_n_txs >= 0
|
|
683
|
++
assert spam_filter_after_n_txs <= 250
|
|
684
|
++
assert xch_spam_amount >= 1
|
|
685
|
++
assert xch_spam_amount <= 10000000000
|
|
686
|
++
assert dust_value >= 1
|
|
687
|
++
assert dust_value <= 10000000000
|
|
688
|
++
|
|
689
|
++
# start both clients
|
|
690
|
++
await farm_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
|
|
691
|
++
await dust_wallet_server.start_client(PeerInfo(self_hostname, full_node_api.full_node.server.get_port()), None)
|
|
692
|
++
|
|
693
|
++
# Farm two blocks
|
|
694
|
++
for i in range(2):
|
|
695
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
722
696
|
|
|
723
|
--
|
|
724
|
--
|
|
697
|
++
# sync both nodes
|
|
698
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
699
|
++
|
|
700
|
++
# Part 1: create a single dust coin
|
|
701
|
++
payees: List[Payment] = []
|
|
702
|
++
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
703
|
++
payees.append(Payment(payee_ph, uint64(dust_value)))
|
|
704
|
++
|
|
705
|
++
# construct and send tx
|
|
706
|
++
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
707
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
708
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
709
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
710
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
711
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
712
|
++
|
|
713
|
++
# The dust is only filtered at this point if spam_filter_after_n_txs is 0 and xch_spam_amount is > dust_value.
|
|
714
|
++
if spam_filter_after_n_txs > 0:
|
|
715
|
++
dust_coins = 1
|
|
716
|
++
large_dust_coins = 0
|
|
717
|
++
large_dust_balance = 0
|
|
718
|
++
elif xch_spam_amount <= dust_value:
|
|
719
|
++
dust_coins = 0
|
|
720
|
++
large_dust_coins = 1
|
|
721
|
++
large_dust_balance = dust_value
|
|
722
|
++
else:
|
|
723
|
++
dust_coins = 0
|
|
724
|
++
large_dust_coins = 0
|
|
725
|
++
large_dust_balance = 0
|
|
726
|
++
|
|
727
|
++
# Obtain and log important values
|
|
728
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
729
|
++
log.info(f"all_unspent is {all_unspent}")
|
|
730
|
++
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
731
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
732
|
++
num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance, DEFAULT_COIN_SELECTION_CONFIG))
|
|
733
|
++
|
|
734
|
++
log.info(f"Small coin count is {small_unspent_count}")
|
|
735
|
++
log.info(f"Wallet balance is {balance}")
|
|
736
|
++
log.info(f"Number of coins is {num_coins}")
|
|
737
|
++
|
|
738
|
++
log.info(f"spam_filter_after_n_txs {spam_filter_after_n_txs}")
|
|
739
|
++
log.info(f"xch_spam_amount {xch_spam_amount}")
|
|
740
|
++
log.info(f"dust_value {dust_value}")
|
|
741
|
++
|
|
742
|
++
# Verify balance and number of coins not filtered.
|
|
743
|
++
assert balance == dust_coins * dust_value + large_dust_balance
|
|
744
|
++
assert num_coins == dust_coins + large_dust_coins
|
|
745
|
++
|
|
746
|
++
# Part 2: Create dust coins until the filter threshold has been reached.
|
|
747
|
++
# Nothing should be filtered yet (unless spam_filter_after_n_txs is 0).
|
|
748
|
++
payees = []
|
|
749
|
++
|
|
750
|
++
# Determine how much dust to create, recalling that there already is one dust coin.
|
|
751
|
++
new_dust = spam_filter_after_n_txs - 1
|
|
752
|
++
dust_remaining = new_dust
|
|
753
|
++
|
|
754
|
++
while dust_remaining > 0:
|
|
725
755
|
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
726
756
|
payees.append(Payment(payee_ph, uint64(dust_value)))
|
|
727
757
|
|
|
728
|
--
#
|
|
729
|
--
|
|
730
|
--
|
|
731
|
--
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
732
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
733
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
734
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
735
|
--
|
|
736
|
--
# The dust is only filtered at this point if spam_filter_after_n_txs is 0 and xch_spam_amount is > dust_value.
|
|
737
|
--
if spam_filter_after_n_txs > 0:
|
|
738
|
--
dust_coins = 1
|
|
739
|
--
large_dust_coins = 0
|
|
740
|
--
large_dust_balance = 0
|
|
741
|
--
elif xch_spam_amount <= dust_value:
|
|
742
|
--
dust_coins = 0
|
|
743
|
--
large_dust_coins = 1
|
|
744
|
--
large_dust_balance = dust_value
|
|
745
|
--
else:
|
|
746
|
--
dust_coins = 0
|
|
747
|
--
large_dust_coins = 0
|
|
748
|
--
large_dust_balance = 0
|
|
749
|
--
|
|
750
|
--
# Obtain and log important values
|
|
751
|
--
all_unspent: Set[
|
|
752
|
--
WalletCoinRecord
|
|
753
|
--
] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
754
|
--
log.info(f"all_unspent is {all_unspent}")
|
|
755
|
--
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
756
|
--
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
757
|
--
num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance, DEFAULT_COIN_SELECTION_CONFIG))
|
|
758
|
--
|
|
759
|
--
log.info(f"Small coin count is {small_unspent_count}")
|
|
760
|
--
log.info(f"Wallet balance is {balance}")
|
|
761
|
--
log.info(f"Number of coins is {num_coins}")
|
|
762
|
--
|
|
763
|
--
log.info(f"spam_filter_after_n_txs {spam_filter_after_n_txs}")
|
|
764
|
--
log.info(f"xch_spam_amount {xch_spam_amount}")
|
|
765
|
--
log.info(f"dust_value {dust_value}")
|
|
766
|
--
|
|
767
|
--
# Verify balance and number of coins not filtered.
|
|
768
|
--
assert balance == dust_coins * dust_value + large_dust_balance
|
|
769
|
--
assert num_coins == dust_coins + large_dust_coins
|
|
770
|
--
|
|
771
|
--
# Part 2: Create dust coins until the filter threshold has been reached.
|
|
772
|
--
# Nothing should be filtered yet (unless spam_filter_after_n_txs is 0).
|
|
773
|
--
payees = []
|
|
774
|
--
|
|
775
|
--
# Determine how much dust to create, recalling that there already is one dust coin.
|
|
776
|
--
new_dust = spam_filter_after_n_txs - 1
|
|
777
|
--
dust_remaining = new_dust
|
|
778
|
--
|
|
779
|
--
while dust_remaining > 0:
|
|
780
|
--
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
781
|
--
payees.append(Payment(payee_ph, uint64(dust_value)))
|
|
782
|
--
|
|
783
|
--
# After every 100 (at most) coins added, push the tx and advance the chain
|
|
784
|
--
# This greatly speeds up the overall process
|
|
785
|
--
if dust_remaining % 100 == 0 and dust_remaining != new_dust:
|
|
786
|
--
# construct and send tx
|
|
787
|
--
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
788
|
--
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
789
|
--
|
|
790
|
--
# advance the chain and sync both wallets
|
|
791
|
--
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
792
|
--
await full_node_api.wait_for_wallets_synced(
|
|
793
|
--
wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
|
|
794
|
--
)
|
|
795
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
796
|
--
await full_node_api.wait_for_wallets_synced(
|
|
797
|
--
wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
|
|
798
|
--
)
|
|
799
|
--
# reset payees
|
|
800
|
--
payees = []
|
|
801
|
--
|
|
802
|
--
dust_remaining -= 1
|
|
803
|
--
|
|
804
|
--
# Only need to create tx if there was new dust to be added
|
|
805
|
--
if new_dust >= 1:
|
|
758
|
++
# After every 100 (at most) coins added, push the tx and advance the chain
|
|
759
|
++
# This greatly speeds up the overall process
|
|
760
|
++
if dust_remaining % 100 == 0 and dust_remaining != new_dust:
|
|
806
761
|
# construct and send tx
|
|
807
762
|
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
808
763
|
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
@@@ -812,44 -812,44 +767,13 @@@
|
|
|
812
767
|
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
813
768
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
814
769
|
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
770
|
++
# reset payees
|
|
771
|
++
payees = []
|
|
815
772
|
|
|
816
|
--
|
|
817
|
--
all_unspent: Set[
|
|
818
|
--
WalletCoinRecord
|
|
819
|
--
] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
820
|
--
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
821
|
--
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
822
|
--
# Selecting coins by using the wallet's coin selection algorithm won't work for large
|
|
823
|
--
# numbers of coins, so we'll use the state manager for the rest of the test
|
|
824
|
--
# num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance))
|
|
825
|
--
num_coins: Optional[Message] = len(
|
|
826
|
--
list(await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1))
|
|
827
|
--
)
|
|
828
|
--
|
|
829
|
--
log.info(f"Small coin count is {small_unspent_count}")
|
|
830
|
--
log.info(f"Wallet balance is {balance}")
|
|
831
|
--
log.info(f"Number of coins is {num_coins}")
|
|
832
|
--
|
|
833
|
--
# obtain the total expected coins (new_dust could be negative)
|
|
834
|
--
if new_dust > 0:
|
|
835
|
--
dust_coins += new_dust
|
|
836
|
--
|
|
837
|
--
# Make sure the number of coins matches the expected number.
|
|
838
|
--
# At this point, nothing should be getting filtered unless spam_filter_after_n_txs is 0.
|
|
839
|
--
assert dust_coins == spam_filter_after_n_txs
|
|
840
|
--
assert balance == dust_coins * dust_value + large_dust_balance
|
|
841
|
--
assert num_coins == dust_coins + large_dust_coins
|
|
842
|
--
|
|
843
|
--
# Part 3: Create 10 coins that are exactly the size of the filter threshold.
|
|
844
|
--
# These should not get filtered.
|
|
845
|
--
large_coins = 10
|
|
846
|
--
|
|
847
|
--
payees = []
|
|
848
|
--
|
|
849
|
--
for i in range(large_coins):
|
|
850
|
--
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
851
|
--
payees.append(Payment(payee_ph, uint64(xch_spam_amount)))
|
|
773
|
++
dust_remaining -= 1
|
|
852
774
|
|
|
775
|
++
# Only need to create tx if there was new dust to be added
|
|
776
|
++
if new_dust >= 1:
|
|
853
777
|
# construct and send tx
|
|
854
778
|
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
855
779
|
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
@@@ -860,485 -860,485 +784,505 @@@
|
|
|
860
784
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
861
785
|
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
862
786
|
|
|
863
|
--
|
|
864
|
--
|
|
865
|
--
|
|
866
|
--
|
|
867
|
--
|
|
868
|
--
|
|
869
|
--
|
|
870
|
--
|
|
871
|
--
|
|
872
|
--
|
|
873
|
--
|
|
874
|
--
|
|
875
|
--
|
|
876
|
--
|
|
877
|
--
|
|
878
|
--
|
|
879
|
--
|
|
880
|
--
|
|
881
|
--
|
|
882
|
--
|
|
883
|
--
|
|
884
|
--
|
|
885
|
--
|
|
886
|
--
|
|
887
|
--
|
|
888
|
--
|
|
787
|
++
# Obtain and log important values
|
|
788
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
789
|
++
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
790
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
791
|
++
# Selecting coins by using the wallet's coin selection algorithm won't work for large
|
|
792
|
++
# numbers of coins, so we'll use the state manager for the rest of the test
|
|
793
|
++
# num_coins: Optional[Message] = len(await dust_wallet.select_coins(balance))
|
|
794
|
++
spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
|
|
795
|
++
num_coins = len(spendable_coins)
|
|
796
|
++
|
|
797
|
++
log.info(f"Small coin count is {small_unspent_count}")
|
|
798
|
++
log.info(f"Wallet balance is {balance}")
|
|
799
|
++
log.info(f"Number of coins is {num_coins}")
|
|
800
|
++
|
|
801
|
++
# obtain the total expected coins (new_dust could be negative)
|
|
802
|
++
if new_dust > 0:
|
|
803
|
++
dust_coins += new_dust
|
|
804
|
++
|
|
805
|
++
# Make sure the number of coins matches the expected number.
|
|
806
|
++
# At this point, nothing should be getting filtered unless spam_filter_after_n_txs is 0.
|
|
807
|
++
assert dust_coins == spam_filter_after_n_txs
|
|
808
|
++
assert balance == dust_coins * dust_value + large_dust_balance
|
|
809
|
++
assert num_coins == dust_coins + large_dust_coins
|
|
810
|
++
|
|
811
|
++
# Part 3: Create 10 coins that are exactly the size of the filter threshold.
|
|
812
|
++
# These should not get filtered.
|
|
813
|
++
large_coins = 10
|
|
814
|
++
|
|
815
|
++
payees = []
|
|
816
|
++
|
|
817
|
++
for i in range(large_coins):
|
|
818
|
++
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
819
|
++
payees.append(Payment(payee_ph, uint64(xch_spam_amount)))
|
|
820
|
++
|
|
821
|
++
# construct and send tx
|
|
822
|
++
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
823
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
824
|
++
|
|
825
|
++
# advance the chain and sync both wallets
|
|
826
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
827
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
828
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
829
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
830
|
++
|
|
831
|
++
# Obtain and log important values
|
|
832
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
833
|
++
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
834
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
835
|
++
spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
|
|
836
|
++
num_coins = len(spendable_coins)
|
|
837
|
++
|
|
838
|
++
log.info(f"Small coin count is {small_unspent_count}")
|
|
839
|
++
log.info(f"Wallet balance is {balance}")
|
|
840
|
++
log.info(f"Number of coins is {num_coins}")
|
|
841
|
++
|
|
842
|
++
large_coin_balance = large_coins * xch_spam_amount
|
|
843
|
++
|
|
844
|
++
# Determine whether the filter should have been activated.
|
|
845
|
++
# Make sure the number of coins matches the expected number.
|
|
846
|
++
# At this point, nothing should be getting filtered unless spam_filter_after_n_txs is 0.
|
|
847
|
++
assert dust_coins == spam_filter_after_n_txs
|
|
848
|
++
assert balance == dust_coins * dust_value + large_coins * xch_spam_amount + large_dust_balance
|
|
849
|
++
assert num_coins == dust_coins + large_coins + large_dust_coins
|
|
850
|
++
|
|
851
|
++
# Part 4: Create one more dust coin to test the threshold
|
|
852
|
++
payees = []
|
|
853
|
++
|
|
854
|
++
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
855
|
++
payees.append(Payment(payee_ph, uint64(dust_value)))
|
|
856
|
++
|
|
857
|
++
# construct and send tx
|
|
858
|
++
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
859
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
860
|
++
|
|
861
|
++
# advance the chain and sync both wallets
|
|
862
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
863
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
864
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
865
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
866
|
++
|
|
867
|
++
# Obtain and log important values
|
|
868
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
869
|
++
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
870
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
871
|
++
spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
|
|
872
|
++
num_coins = len(spendable_coins)
|
|
873
|
++
|
|
874
|
++
log.info(f"Small coin count is {small_unspent_count}")
|
|
875
|
++
log.info(f"Wallet balance is {balance}")
|
|
876
|
++
log.info(f"Number of coins is {num_coins}")
|
|
877
|
++
|
|
878
|
++
# In the edge case where the new "dust" is larger than the threshold,
|
|
879
|
++
# then it is actually a large dust coin that won't get filtered.
|
|
880
|
++
if dust_value >= xch_spam_amount:
|
|
881
|
++
large_dust_coins += 1
|
|
882
|
++
large_dust_balance += dust_value
|
|
883
|
++
|
|
884
|
++
assert dust_coins == spam_filter_after_n_txs
|
|
885
|
++
assert balance == dust_coins * dust_value + large_coins * xch_spam_amount + large_dust_balance
|
|
886
|
++
assert num_coins == dust_coins + large_dust_coins + large_coins
|
|
887
|
++
|
|
888
|
++
# Part 5: Create 5 coins below the threshold and 5 at or above.
|
|
889
|
++
# Those below the threshold should get filtered, and those above should not.
|
|
890
|
++
payees = []
|
|
891
|
++
|
|
892
|
++
for i in range(5):
|
|
889
893
|
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
890
|
--
payees.append(Payment(payee_ph, uint64(dust_value)))
|
|
891
|
--
|
|
892
|
--
# construct and send tx
|
|
893
|
--
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
894
|
--
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
895
|
--
|
|
896
|
--
# advance the chain and sync both wallets
|
|
897
|
--
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
898
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
899
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
900
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
901
894
|
|
|
902
|
--
#
|
|
903
|
--
|
|
904
|
--
|
|
905
|
--
|
|
906
|
--
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
907
|
--
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
908
|
--
num_coins: Optional[Message] = len(
|
|
909
|
--
list(await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1))
|
|
910
|
--
)
|
|
895
|
++
# Create a large coin and add on the appropriate balance.
|
|
896
|
++
payees.append(Payment(payee_ph, uint64(xch_spam_amount + i)))
|
|
897
|
++
large_coins += 1
|
|
898
|
++
large_coin_balance += xch_spam_amount + i
|
|
911
899
|
|
|
912
|
--
|
|
913
|
--
log.info(f"Wallet balance is {balance}")
|
|
914
|
--
log.info(f"Number of coins is {num_coins}")
|
|
900
|
++
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
915
901
|
|
|
916
|
--
#
|
|
917
|
--
|
|
918
|
--
|
|
902
|
++
# Make sure we are always creating coins with a positive value.
|
|
903
|
++
if xch_spam_amount - dust_value - i > 0:
|
|
904
|
++
payees.append(Payment(payee_ph, uint64(xch_spam_amount - dust_value - i)))
|
|
905
|
++
else:
|
|
906
|
++
payees.append(Payment(payee_ph, uint64(dust_value)))
|
|
907
|
++
# In cases where xch_spam_amount is sufficiently low,
|
|
908
|
++
# the new dust should be considered a large coina and not be filtered.
|
|
909
|
++
if xch_spam_amount <= dust_value:
|
|
919
910
|
large_dust_coins += 1
|
|
920
911
|
large_dust_balance += dust_value
|
|
921
912
|
|
|
922
|
--
|
|
923
|
--
|
|
924
|
--
|
|
925
|
--
|
|
926
|
--
|
|
927
|
--
|
|
928
|
--
|
|
929
|
--
|
|
930
|
--
|
|
931
|
--
|
|
913
|
++
# construct and send tx
|
|
914
|
++
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
915
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
916
|
++
|
|
917
|
++
# advance the chain and sync both wallets
|
|
918
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
919
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
920
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
921
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
922
|
++
|
|
923
|
++
# Obtain and log important values
|
|
924
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
925
|
++
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
926
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
927
|
++
spendable_coins = await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1)
|
|
928
|
++
num_coins = len(spendable_coins)
|
|
929
|
++
|
|
930
|
++
log.info(f"Small coin count is {small_unspent_count}")
|
|
931
|
++
log.info(f"Wallet balance is {balance}")
|
|
932
|
++
log.info(f"Number of coins is {num_coins}")
|
|
933
|
++
|
|
934
|
++
# The filter should have automatically been activated by now, regardless of filter value
|
|
935
|
++
assert dust_coins == spam_filter_after_n_txs
|
|
936
|
++
assert balance == dust_coins * dust_value + large_coin_balance + large_dust_balance
|
|
937
|
++
assert num_coins == dust_coins + large_dust_coins + large_coins
|
|
938
|
++
|
|
939
|
++
# Part 6: Clear all coins from the dust wallet.
|
|
940
|
++
# Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
|
|
941
|
++
# Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
|
|
942
|
++
|
|
943
|
++
payee_ph = await farm_wallet.get_new_puzzlehash()
|
|
944
|
++
payees = [Payment(payee_ph, uint64(balance))]
|
|
945
|
++
|
|
946
|
++
# construct and send tx
|
|
947
|
++
[tx] = await dust_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
948
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
949
|
++
|
|
950
|
++
# advance the chain and sync both wallets
|
|
951
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
952
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
953
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
954
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
955
|
++
|
|
956
|
++
# Obtain and log important values
|
|
957
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
958
|
++
unspent_count = len(all_unspent)
|
|
959
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
960
|
++
|
|
961
|
++
# Make sure the dust wallet is empty
|
|
962
|
++
assert unspent_count == 0
|
|
963
|
++
assert balance == 0
|
|
964
|
++
|
|
965
|
++
# create the same number of dust coins as the filter
|
|
966
|
++
if spam_filter_after_n_txs > 0:
|
|
967
|
++
coins_remaining = spam_filter_after_n_txs
|
|
968
|
++
else:
|
|
969
|
++
# in the edge case, create one coin
|
|
970
|
++
coins_remaining = 1
|
|
971
|
++
|
|
972
|
++
# The size of the coin to send the dust wallet is the same as xch_spam_amount
|
|
973
|
++
if xch_spam_amount > 1:
|
|
974
|
++
coin_value = xch_spam_amount
|
|
975
|
++
else:
|
|
976
|
++
# Handle the edge case to make sure the coin is at least 2 mojos
|
|
977
|
++
# This is needed to receive change
|
|
978
|
++
coin_value = 2
|
|
979
|
++
|
|
980
|
++
while coins_remaining > 0:
|
|
981
|
++
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
982
|
++
payees.append(Payment(payee_ph, uint64(coin_value)))
|
|
932
983
|
|
|
933
|
--
|
|
934
|
--
|
|
935
|
--
|
|
936
|
--
|
|
984
|
++
# After every 100 (at most) coins added, push the tx and advance the chain
|
|
985
|
++
# This greatly speeds up the overall process
|
|
986
|
++
if coins_remaining % 100 == 0 and coins_remaining != spam_filter_after_n_txs:
|
|
987
|
++
# construct and send tx
|
|
988
|
++
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
989
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
990
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
991
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
992
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
993
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
994
|
++
# reset payees
|
|
995
|
++
payees = []
|
|
996
|
++
|
|
997
|
++
coins_remaining -= 1
|
|
998
|
++
|
|
999
|
++
# construct and send tx
|
|
1000
|
++
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
1001
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
1002
|
++
|
|
1003
|
++
# advance the chain and sync both wallets
|
|
1004
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
1005
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1006
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1007
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1008
|
++
|
|
1009
|
++
# Obtain and log important values
|
|
1010
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
1011
|
++
unspent_count = len(all_unspent)
|
|
1012
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
1013
|
++
|
|
1014
|
++
# Verify the number of coins and value
|
|
1015
|
++
if spam_filter_after_n_txs > 0:
|
|
1016
|
++
assert unspent_count == spam_filter_after_n_txs
|
|
1017
|
++
else:
|
|
1018
|
++
# in the edge case there should be 1 coin
|
|
1019
|
++
assert unspent_count == 1
|
|
1020
|
++
assert balance == unspent_count * coin_value
|
|
1021
|
++
|
|
1022
|
++
# Send a 1 mojo coin from the dust wallet to the farm wallet
|
|
1023
|
++
payee_ph = await farm_wallet.get_new_puzzlehash()
|
|
1024
|
++
payees = [Payment(payee_ph, uint64(1))]
|
|
1025
|
++
|
|
1026
|
++
# construct and send tx
|
|
1027
|
++
[tx] = await dust_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
1028
|
++
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
1029
|
++
|
|
1030
|
++
# advance the chain and sync both wallets
|
|
1031
|
++
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
1032
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1033
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1034
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1035
|
++
|
|
1036
|
++
# Obtain and log important values
|
|
1037
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
1038
|
++
unspent_count = len(all_unspent)
|
|
1039
|
++
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
1040
|
++
|
|
1041
|
++
# Make sure the dust wallet received a change coin worth 1 mojo less than the original coin size
|
|
1042
|
++
if spam_filter_after_n_txs > 0:
|
|
1043
|
++
assert unspent_count == spam_filter_after_n_txs
|
|
1044
|
++
else:
|
|
1045
|
++
# in the edge case there should be 1 coin
|
|
1046
|
++
assert unspent_count == 1
|
|
1047
|
++
assert balance == (unspent_count * coin_value) - 1
|
|
1048
|
++
|
|
1049
|
++
# Part 7: Create NFT wallets for the farmer and dust wallets.
|
|
1050
|
++
# Generate an NFT in the farmer wallet.
|
|
1051
|
++
# Send the NFT to the dust wallet, which already has enough coins to trigger the dust filter.
|
|
1052
|
++
# The NFT should not be filtered.
|
|
1053
|
++
|
|
1054
|
++
# Start with new puzzlehashes for each wallet
|
|
1055
|
++
farm_ph = await farm_wallet.get_new_puzzlehash()
|
|
1056
|
++
dust_ph = await dust_wallet.get_new_puzzlehash()
|
|
1057
|
++
|
|
1058
|
++
# Create an NFT wallet for the farmer and dust wallet
|
|
1059
|
++
farm_nft_wallet = await NFTWallet.create_new_nft_wallet(
|
|
1060
|
++
farm_wallet_node.wallet_state_manager, farm_wallet, name="FARM NFT WALLET"
|
|
1061
|
++
)
|
|
1062
|
++
dust_nft_wallet = await NFTWallet.create_new_nft_wallet(
|
|
1063
|
++
dust_wallet_node.wallet_state_manager, dust_wallet, name="DUST NFT WALLET"
|
|
1064
|
++
)
|
|
937
1065
|
|
|
938
|
--
|
|
1066
|
++
# Create a new NFT and send it to the farmer's NFT wallet
|
|
1067
|
++
metadata = Program.to(
|
|
1068
|
++
[
|
|
1069
|
++
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
|
1070
|
++
("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
|
|
1071
|
++
]
|
|
1072
|
++
)
|
|
1073
|
++
txs = await farm_nft_wallet.generate_new_nft(metadata, DEFAULT_TX_CONFIG)
|
|
1074
|
++
txs = await farm_nft_wallet.wallet_state_manager.add_pending_transactions(txs)
|
|
1075
|
++
for tx in txs:
|
|
1076
|
++
if tx.spend_bundle is not None:
|
|
1077
|
++
assert compute_memos(tx.spend_bundle)
|
|
1078
|
++
await time_out_assert_not_none(
|
|
1079
|
++
20, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
|
|
1080
|
++
)
|
|
939
1081
|
|
|
940
|
--
|
|
941
|
--
|
|
942
|
--
|
|
1082
|
++
# Farm a new block
|
|
1083
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1084
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
|
|
1085
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1086
|
++
|
|
1087
|
++
# Make sure the dust wallet has enough unspent coins in that the next coin would be filtered
|
|
1088
|
++
# if it were a normal dust coin (and not an NFT)
|
|
1089
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
1090
|
++
unspent_count = len(all_unspent)
|
|
1091
|
++
assert unspent_count >= spam_filter_after_n_txs
|
|
1092
|
++
|
|
1093
|
++
# Make sure the NFT is in the farmer's NFT wallet, and the dust NFT wallet is empty
|
|
1094
|
++
await time_out_assert(15, get_nft_count, 1, farm_nft_wallet)
|
|
1095
|
++
await time_out_assert(15, get_nft_count, 0, dust_nft_wallet)
|
|
1096
|
++
|
|
1097
|
++
nft_coins = await farm_nft_wallet.get_current_nfts()
|
|
1098
|
++
# Send the NFT to the dust wallet
|
|
1099
|
++
txs = await farm_nft_wallet.generate_signed_transaction(
|
|
1100
|
++
[uint64(nft_coins[0].coin.amount)],
|
|
1101
|
++
[dust_ph],
|
|
1102
|
++
DEFAULT_TX_CONFIG,
|
|
1103
|
++
coins={nft_coins[0].coin},
|
|
1104
|
++
)
|
|
1105
|
++
assert len(txs) == 1
|
|
1106
|
++
assert txs[0].spend_bundle is not None
|
|
1107
|
++
await farm_wallet_node.wallet_state_manager.add_pending_transactions(txs)
|
|
1108
|
++
assert compute_memos(txs[0].spend_bundle)
|
|
1109
|
++
|
|
1110
|
++
# Farm a new block.
|
|
1111
|
++
await full_node_api.wait_transaction_records_entered_mempool(txs)
|
|
1112
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1113
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
|
|
1114
|
++
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1115
|
++
|
|
1116
|
++
# Make sure the dust wallet has enough unspent coins in that the next coin would be filtered
|
|
1117
|
++
# if it were a normal dust coin (and not an NFT)
|
|
1118
|
++
all_unspent: Set[WalletCoinRecord] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
1119
|
++
unspent_count = len(all_unspent)
|
|
1120
|
++
assert unspent_count >= spam_filter_after_n_txs
|
|
1121
|
++
|
|
1122
|
++
# The dust wallet should now hold the NFT. It should not be filtered
|
|
1123
|
++
await time_out_assert(15, get_nft_count, 0, farm_nft_wallet)
|
|
1124
|
++
await time_out_assert(15, get_nft_count, 1, dust_nft_wallet)
|
|
1125
|
++
|
|
1126
|
++
|
|
1127
|
++
@pytest.mark.anyio
|
|
1128
|
++
async def test_retry_store(two_wallet_nodes, self_hostname):
|
|
1129
|
++
full_nodes, wallets, bt = two_wallet_nodes
|
|
1130
|
++
full_node_api = full_nodes[0]
|
|
1131
|
++
full_node_server = full_node_api.full_node.server
|
|
1132
|
++
|
|
1133
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
|
|
1134
|
++
|
|
1135
|
++
# Trusted node sync
|
|
1136
|
++
wallets[0][0].config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
|
|
1137
|
++
|
|
1138
|
++
# Untrusted node sync
|
|
1139
|
++
wallets[1][0].config["trusted_peers"] = {}
|
|
1140
|
++
|
|
1141
|
++
def flaky_get_coin_state(node, func):
|
|
1142
|
++
async def new_func(*args, **kwargs):
|
|
1143
|
++
if node.coin_state_flaky:
|
|
1144
|
++
node.coin_state_flaky = False
|
|
1145
|
++
raise PeerRequestException()
|
|
943
1146
|
else:
|
|
944
|
--
|
|
945
|
--
# In cases where xch_spam_amount is sufficiently low,
|
|
946
|
--
# the new dust should be considered a large coina and not be filtered.
|
|
947
|
--
if xch_spam_amount <= dust_value:
|
|
948
|
--
large_dust_coins += 1
|
|
949
|
--
large_dust_balance += dust_value
|
|
950
|
--
|
|
951
|
--
# construct and send tx
|
|
952
|
--
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
953
|
--
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
1147
|
++
return await func(*args, **kwargs)
|
|
954
1148
|
|
|
955
|
--
|
|
956
|
--
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
957
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
958
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
959
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
960
|
--
|
|
961
|
--
# Obtain and log important values
|
|
962
|
--
all_unspent: Set[
|
|
963
|
--
WalletCoinRecord
|
|
964
|
--
] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
965
|
--
small_unspent_count = len([r for r in all_unspent if r.coin.amount < xch_spam_amount])
|
|
966
|
--
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
967
|
--
num_coins: Optional[Message] = len(
|
|
968
|
--
list(await dust_wallet_node.wallet_state_manager.get_spendable_coins_for_wallet(1))
|
|
969
|
--
)
|
|
970
|
--
|
|
971
|
--
log.info(f"Small coin count is {small_unspent_count}")
|
|
972
|
--
log.info(f"Wallet balance is {balance}")
|
|
973
|
--
log.info(f"Number of coins is {num_coins}")
|
|
974
|
--
|
|
975
|
--
# The filter should have automatically been activated by now, regardless of filter value
|
|
976
|
--
assert dust_coins == spam_filter_after_n_txs
|
|
977
|
--
assert balance == dust_coins * dust_value + large_coin_balance + large_dust_balance
|
|
978
|
--
assert num_coins == dust_coins + large_dust_coins + large_coins
|
|
979
|
--
|
|
980
|
--
# Part 6: Clear all coins from the dust wallet.
|
|
981
|
--
# Send to the dust wallet "spam_filter_after_n_txs" coins that are equal in value to "xch_spam_amount".
|
|
982
|
--
# Send 1 mojo from the dust wallet. The dust wallet should receive a change coin valued at "xch_spam_amount-1".
|
|
983
|
--
|
|
984
|
--
payee_ph = await farm_wallet.get_new_puzzlehash()
|
|
985
|
--
payees = [Payment(payee_ph, uint64(balance))]
|
|
986
|
--
|
|
987
|
--
# construct and send tx
|
|
988
|
--
[tx] = await dust_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
989
|
--
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
1149
|
++
return new_func
|
|
990
1150
|
|
|
991
|
--
|
|
992
|
--
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
993
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
994
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
995
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
996
|
--
|
|
997
|
--
# Obtain and log important values
|
|
998
|
--
all_unspent: Set[
|
|
999
|
--
WalletCoinRecord
|
|
1000
|
--
] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
1001
|
--
unspent_count = len([r for r in all_unspent])
|
|
1002
|
--
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
1003
|
--
|
|
1004
|
--
# Make sure the dust wallet is empty
|
|
1005
|
--
assert unspent_count == 0
|
|
1006
|
--
assert balance == 0
|
|
1007
|
--
|
|
1008
|
--
# create the same number of dust coins as the filter
|
|
1009
|
--
if spam_filter_after_n_txs > 0:
|
|
1010
|
--
coins_remaining = spam_filter_after_n_txs
|
|
1011
|
--
else:
|
|
1012
|
--
# in the edge case, create one coin
|
|
1013
|
--
coins_remaining = 1
|
|
1014
|
--
|
|
1015
|
--
# The size of the coin to send the dust wallet is the same as xch_spam_amount
|
|
1016
|
--
if xch_spam_amount > 1:
|
|
1017
|
--
coin_value = xch_spam_amount
|
|
1018
|
--
else:
|
|
1019
|
--
# Handle the edge case to make sure the coin is at least 2 mojos
|
|
1020
|
--
# This is needed to receive change
|
|
1021
|
--
coin_value = 2
|
|
1022
|
--
|
|
1023
|
--
while coins_remaining > 0:
|
|
1024
|
--
payee_ph = await dust_wallet.get_new_puzzlehash()
|
|
1025
|
--
payees.append(Payment(payee_ph, uint64(coin_value)))
|
|
1026
|
--
|
|
1027
|
--
# After every 100 (at most) coins added, push the tx and advance the chain
|
|
1028
|
--
# This greatly speeds up the overall process
|
|
1029
|
--
if coins_remaining % 100 == 0 and coins_remaining != spam_filter_after_n_txs:
|
|
1030
|
--
# construct and send tx
|
|
1031
|
--
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
1032
|
--
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
1033
|
--
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
1034
|
--
await full_node_api.wait_for_wallets_synced(
|
|
1035
|
--
wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
|
|
1036
|
--
)
|
|
1037
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1038
|
--
await full_node_api.wait_for_wallets_synced(
|
|
1039
|
--
wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20
|
|
1040
|
--
)
|
|
1041
|
--
# reset payees
|
|
1042
|
--
payees = []
|
|
1043
|
--
|
|
1044
|
--
coins_remaining -= 1
|
|
1045
|
--
|
|
1046
|
--
# construct and send tx
|
|
1047
|
--
[tx] = await farm_wallet.generate_signed_transaction(uint64(0), ph, DEFAULT_TX_CONFIG, primaries=payees)
|
|
1048
|
--
await full_node_api.send_transaction(SendTransaction(tx.spend_bundle))
|
|
1151
|
++
request_puzzle_solution_failure_tested = False
|
|
1049
1152
|
|
|
1050
|
--
|
|
1051
|
--
|
|
1052
|
--
|
|
1053
|
--
|
|
1054
|
--
|
|
1153
|
++
def flaky_request_puzzle_solution(func):
|
|
1154
|
++
@functools.wraps(func)
|
|
1155
|
++
async def new_func(*args, **kwargs):
|
|
1156
|
++
nonlocal request_puzzle_solution_failure_tested
|
|
1157
|
++
if not request_puzzle_solution_failure_tested:
|
|
1158
|
++
request_puzzle_solution_failure_tested = True
|
|
1159
|
++
# This can just return None if we have `none_response` enabled.
|
|
1160
|
++
reject = wallet_protocol.RejectPuzzleSolution(bytes32([0] * 32), uint32(0))
|
|
1161
|
++
return make_msg(ProtocolMessageTypes.reject_puzzle_solution, reject)
|
|
1162
|
++
else:
|
|
1163
|
++
return await func(*args, **kwargs)
|
|
1055
1164
|
|
|
1056
|
--
|
|
1057
|
--
all_unspent: Set[
|
|
1058
|
--
WalletCoinRecord
|
|
1059
|
--
] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
1060
|
--
unspent_count = len([r for r in all_unspent])
|
|
1061
|
--
balance: Optional[Message] = await dust_wallet.get_confirmed_balance()
|
|
1165
|
++
return new_func
|
|
1062
1166
|
|
|
1063
|
--
|
|
1064
|
--
|
|
1065
|
--
|
|
1066
|
--
|
|
1067
|
--
|
|
1068
|
--
|
|
1069
|
--
|
|
1167
|
++
def flaky_fetch_children(node, func):
|
|
1168
|
++
async def new_func(*args, **kwargs):
|
|
1169
|
++
if node.fetch_children_flaky:
|
|
1170
|
++
node.fetch_children_flaky = False
|
|
1171
|
++
raise PeerRequestException()
|
|
1172
|
++
else:
|
|
1173
|
++
return await func(*args, **kwargs)
|
|
1070
1174
|
|
|
1071
|
--
|
|
1072
|
--
payee_ph = await farm_wallet.get_new_puzzlehash()
|
|
1073
|
--
payees = [Payment(payee_ph, uint64(1))]
|
|
1175
|
++
return new_func
|
|
1074
1176
|
|
|
1075
|
--
|
|
1076
|
--
|
|
1077
|
--
|
|
1177
|
++
def flaky_get_timestamp(node, func):
|
|
1178
|
++
async def new_func(*args, **kwargs):
|
|
1179
|
++
if node.get_timestamp_flaky:
|
|
1180
|
++
node.get_timestamp_flaky = False
|
|
1181
|
++
raise PeerRequestException()
|
|
1182
|
++
else:
|
|
1183
|
++
return await func(*args, **kwargs)
|
|
1078
1184
|
|
|
1079
|
--
|
|
1080
|
--
await full_node_api.wait_transaction_records_entered_mempool([tx])
|
|
1081
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1082
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1083
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1185
|
++
return new_func
|
|
1084
1186
|
|
|
1085
|
--
|
|
1086
|
--
|
|
1087
|
--
|
|
1088
|
--
|
|
1089
|
--
|
|
1090
|
--
|
|
1187
|
++
def flaky_info_for_puzhash(node, func):
|
|
1188
|
++
async def new_func(*args, **kwargs):
|
|
1189
|
++
if node.db_flaky:
|
|
1190
|
++
node.db_flaky = False
|
|
1191
|
++
raise AIOSqliteError()
|
|
1192
|
++
else:
|
|
1193
|
++
return await func(*args, **kwargs)
|
|
1091
1194
|
|
|
1092
|
--
|
|
1093
|
--
if spam_filter_after_n_txs > 0:
|
|
1094
|
--
assert unspent_count == spam_filter_after_n_txs
|
|
1095
|
--
else:
|
|
1096
|
--
# in the edge case there should be 1 coin
|
|
1097
|
--
assert unspent_count == 1
|
|
1098
|
--
assert balance == (unspent_count * coin_value) - 1
|
|
1099
|
--
|
|
1100
|
--
# Part 7: Create NFT wallets for the farmer and dust wallets.
|
|
1101
|
--
# Generate an NFT in the farmer wallet.
|
|
1102
|
--
# Send the NFT to the dust wallet, which already has enough coins to trigger the dust filter.
|
|
1103
|
--
# The NFT should not be filtered.
|
|
1104
|
--
|
|
1105
|
--
# Start with new puzzlehashes for each wallet
|
|
1106
|
--
farm_ph = await farm_wallet.get_new_puzzlehash()
|
|
1107
|
--
dust_ph = await dust_wallet.get_new_puzzlehash()
|
|
1108
|
--
|
|
1109
|
--
# Create an NFT wallet for the farmer and dust wallet
|
|
1110
|
--
farm_nft_wallet = await NFTWallet.create_new_nft_wallet(
|
|
1111
|
--
farm_wallet_node.wallet_state_manager, farm_wallet, name="FARM NFT WALLET"
|
|
1112
|
--
)
|
|
1113
|
--
dust_nft_wallet = await NFTWallet.create_new_nft_wallet(
|
|
1114
|
--
dust_wallet_node.wallet_state_manager, dust_wallet, name="DUST NFT WALLET"
|
|
1115
|
--
)
|
|
1195
|
++
return new_func
|
|
1116
1196
|
|
|
1117
|
--
|
|
1118
|
--
metadata = Program.to(
|
|
1119
|
--
[
|
|
1120
|
--
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
|
1121
|
--
("h", "0xD4584AD463139FA8C0D9F68F4B59F185"),
|
|
1122
|
--
]
|
|
1123
|
--
)
|
|
1124
|
--
txs = await farm_nft_wallet.generate_new_nft(metadata, DEFAULT_TX_CONFIG)
|
|
1125
|
--
await farm_nft_wallet.wallet_state_manager.add_pending_transactions(txs)
|
|
1126
|
--
for tx in txs:
|
|
1127
|
--
if tx.spend_bundle is not None:
|
|
1128
|
--
assert compute_memos(tx.spend_bundle)
|
|
1129
|
--
await time_out_assert_not_none(
|
|
1130
|
--
20, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle.name()
|
|
1131
|
--
)
|
|
1132
|
--
|
|
1133
|
--
# Farm a new block
|
|
1134
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1135
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
|
|
1136
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1197
|
++
full_node_api.request_puzzle_solution = flaky_request_puzzle_solution(full_node_api.request_puzzle_solution)
|
|
1137
1198
|
|
|
1138
|
--
|
|
1139
|
--
|
|
1140
|
--
|
|
1141
|
--
|
|
1142
|
--
|
|
1143
|
--
|
|
1144
|
--
|
|
1145
|
--
|
|
1146
|
--
|
|
1147
|
--
|
|
1148
|
--
|
|
1149
|
--
|
|
1150
|
--
|
|
1151
|
--
# Send the NFT to the dust wallet
|
|
1152
|
--
txs = await farm_nft_wallet.generate_signed_transaction(
|
|
1153
|
--
[uint64(nft_coins[0].coin.amount)],
|
|
1154
|
--
[dust_ph],
|
|
1155
|
--
DEFAULT_TX_CONFIG,
|
|
1156
|
--
coins={nft_coins[0].coin},
|
|
1199
|
++
for wallet_node, wallet_server in wallets:
|
|
1200
|
++
wallet_node.coin_state_retry_seconds = 1
|
|
1201
|
++
request_puzzle_solution_failure_tested = False
|
|
1202
|
++
wallet_node.coin_state_flaky = True
|
|
1203
|
++
wallet_node.fetch_children_flaky = True
|
|
1204
|
++
wallet_node.get_timestamp_flaky = True
|
|
1205
|
++
wallet_node.db_flaky = True
|
|
1206
|
++
|
|
1207
|
++
wallet_node.get_coin_state = flaky_get_coin_state(wallet_node, wallet_node.get_coin_state)
|
|
1208
|
++
wallet_node.fetch_children = flaky_fetch_children(wallet_node, wallet_node.fetch_children)
|
|
1209
|
++
wallet_node.get_timestamp_for_height = flaky_get_timestamp(wallet_node, wallet_node.get_timestamp_for_height)
|
|
1210
|
++
wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash = flaky_info_for_puzhash(
|
|
1211
|
++
wallet_node, wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash
|
|
1157
1212
|
)
|
|
1158
|
--
assert len(txs) == 1
|
|
1159
|
--
assert txs[0].spend_bundle is not None
|
|
1160
|
--
await farm_wallet_node.wallet_state_manager.add_pending_transactions(txs)
|
|
1161
|
--
assert compute_memos(txs[0].spend_bundle)
|
|
1162
|
--
|
|
1163
|
--
# Farm a new block.
|
|
1164
|
--
await full_node_api.wait_transaction_records_entered_mempool(txs)
|
|
1165
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1166
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(farm_ph))
|
|
1167
|
--
await full_node_api.wait_for_wallets_synced(wallet_nodes=[farm_wallet_node, dust_wallet_node], timeout=20)
|
|
1168
|
--
|
|
1169
|
--
# Make sure the dust wallet has enough unspent coins in that the next coin would be filtered
|
|
1170
|
--
# if it were a normal dust coin (and not an NFT)
|
|
1171
|
--
all_unspent: Set[
|
|
1172
|
--
WalletCoinRecord
|
|
1173
|
--
] = await dust_wallet_node.wallet_state_manager.coin_store.get_all_unspent_coins()
|
|
1174
|
--
unspent_count = len([r for r in all_unspent])
|
|
1175
|
--
assert unspent_count >= spam_filter_after_n_txs
|
|
1176
|
--
|
|
1177
|
--
# The dust wallet should now hold the NFT. It should not be filtered
|
|
1178
|
--
await time_out_assert(15, get_nft_count, 0, farm_nft_wallet)
|
|
1179
|
--
await time_out_assert(15, get_nft_count, 1, dust_nft_wallet)
|
|
1180
1213
|
|
|
1181
|
--
|
|
1182
|
--
async def test_retry_store(self, two_wallet_nodes, self_hostname):
|
|
1183
|
--
full_nodes, wallets, bt = two_wallet_nodes
|
|
1184
|
--
full_node_api = full_nodes[0]
|
|
1185
|
--
full_node_server = full_node_api.full_node.server
|
|
1214
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
1186
1215
|
|
|
1216
|
++
wallet = wallet_node.wallet_state_manager.main_wallet
|
|
1217
|
++
ph = await wallet.get_new_puzzlehash()
|
|
1218
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1187
1219
|
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
|
|
1188
1220
|
|
|
1189
|
--
|
|
1190
|
--
|
|
1191
|
--
|
|
1192
|
--
# Untrusted node sync
|
|
1193
|
--
wallets[1][0].config["trusted_peers"] = {}
|
|
1221
|
++
async def retry_store_empty() -> bool:
|
|
1222
|
++
return len(await wallet_node.wallet_state_manager.retry_store.get_all_states_to_retry()) == 0
|
|
1194
1223
|
|
|
1195
|
--
def
|
|
1196
|
--
|
|
1197
|
--
|
|
1198
|
--
|
|
1199
|
--
|
|
1200
|
--
else:
|
|
1201
|
--
return await func(*args, **kwargs)
|
|
1224
|
++
async def assert_coin_state_retry() -> None:
|
|
1225
|
++
# Wait for retry coin states to show up
|
|
1226
|
++
await time_out_assert(15, retry_store_empty, False)
|
|
1227
|
++
# And become retried/removed
|
|
1228
|
++
await time_out_assert(30, retry_store_empty, True)
|
|
1202
1229
|
|
|
1203
|
--
|
|
1204
|
--
|
|
1205
|
--
request_puzzle_solution_failure_tested = False
|
|
1206
|
--
|
|
1207
|
--
def flaky_request_puzzle_solution(func):
|
|
1208
|
--
@functools.wraps(func)
|
|
1209
|
--
async def new_func(*args, **kwargs):
|
|
1210
|
--
nonlocal request_puzzle_solution_failure_tested
|
|
1211
|
--
if not request_puzzle_solution_failure_tested:
|
|
1212
|
--
request_puzzle_solution_failure_tested = True
|
|
1213
|
--
# This can just return None if we have `none_response` enabled.
|
|
1214
|
--
reject = wallet_protocol.RejectPuzzleSolution(bytes32([0] * 32), uint32(0))
|
|
1215
|
--
return make_msg(ProtocolMessageTypes.reject_puzzle_solution, reject)
|
|
1216
|
--
else:
|
|
1217
|
--
return await func(*args, **kwargs)
|
|
1218
|
--
|
|
1219
|
--
return new_func
|
|
1220
|
--
|
|
1221
|
--
def flaky_fetch_children(node, func):
|
|
1222
|
--
async def new_func(*args, **kwargs):
|
|
1223
|
--
if node.fetch_children_flaky:
|
|
1224
|
--
node.fetch_children_flaky = False
|
|
1225
|
--
raise PeerRequestException()
|
|
1226
|
--
else:
|
|
1227
|
--
return await func(*args, **kwargs)
|
|
1228
|
--
|
|
1229
|
--
return new_func
|
|
1230
|
--
|
|
1231
|
--
def flaky_get_timestamp(node, func):
|
|
1232
|
--
async def new_func(*args, **kwargs):
|
|
1233
|
--
if node.get_timestamp_flaky:
|
|
1234
|
--
node.get_timestamp_flaky = False
|
|
1235
|
--
raise PeerRequestException()
|
|
1236
|
--
else:
|
|
1237
|
--
return await func(*args, **kwargs)
|
|
1238
|
--
|
|
1239
|
--
return new_func
|
|
1240
|
--
|
|
1241
|
--
def flaky_info_for_puzhash(node, func):
|
|
1242
|
--
async def new_func(*args, **kwargs):
|
|
1243
|
--
if node.db_flaky:
|
|
1244
|
--
node.db_flaky = False
|
|
1245
|
--
raise AIOSqliteError()
|
|
1246
|
--
else:
|
|
1247
|
--
return await func(*args, **kwargs)
|
|
1248
|
--
|
|
1249
|
--
return new_func
|
|
1250
|
--
|
|
1251
|
--
full_node_api.request_puzzle_solution = flaky_request_puzzle_solution(full_node_api.request_puzzle_solution)
|
|
1252
|
--
|
|
1253
|
--
for wallet_node, wallet_server in wallets:
|
|
1254
|
--
wallet_node.coin_state_retry_seconds = 1
|
|
1255
|
--
request_puzzle_solution_failure_tested = False
|
|
1256
|
--
wallet_node.coin_state_flaky = True
|
|
1257
|
--
wallet_node.fetch_children_flaky = True
|
|
1258
|
--
wallet_node.get_timestamp_flaky = True
|
|
1259
|
--
wallet_node.db_flaky = True
|
|
1260
|
--
|
|
1261
|
--
wallet_node.get_coin_state = flaky_get_coin_state(wallet_node, wallet_node.get_coin_state)
|
|
1262
|
--
wallet_node.fetch_children = flaky_fetch_children(wallet_node, wallet_node.fetch_children)
|
|
1263
|
--
wallet_node.get_timestamp_for_height = flaky_get_timestamp(
|
|
1264
|
--
wallet_node, wallet_node.get_timestamp_for_height
|
|
1265
|
--
)
|
|
1266
|
--
wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash = (
|
|
1267
|
--
flaky_info_for_puzhash(
|
|
1268
|
--
wallet_node, wallet_node.wallet_state_manager.puzzle_store.get_wallet_identifier_for_puzzle_hash
|
|
1269
|
--
)
|
|
1270
|
--
)
|
|
1230
|
++
await assert_coin_state_retry()
|
|
1271
1231
|
|
|
1272
|
--
|
|
1232
|
++
await time_out_assert(30, wallet.get_confirmed_balance, 2_000_000_000_000)
|
|
1273
1233
|
|
|
1274
|
--
|
|
1275
|
--
|
|
1276
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
|
1277
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
|
|
1278
|
--
|
|
1279
|
--
async def retry_store_empty() -> bool:
|
|
1280
|
--
return len(await wallet_node.wallet_state_manager.retry_store.get_all_states_to_retry()) == 0
|
|
1281
|
--
|
|
1282
|
--
async def assert_coin_state_retry() -> None:
|
|
1283
|
--
# Wait for retry coin states to show up
|
|
1284
|
--
await time_out_assert(15, retry_store_empty, False)
|
|
1285
|
--
# And become retried/removed
|
|
1286
|
--
await time_out_assert(30, retry_store_empty, True)
|
|
1287
|
--
|
|
1288
|
--
await assert_coin_state_retry()
|
|
1289
|
--
|
|
1290
|
--
await time_out_assert(30, wallet.get_confirmed_balance, 2_000_000_000_000)
|
|
1291
|
--
|
|
1292
|
--
[tx] = await wallet.generate_signed_transaction(
|
|
1293
|
--
1_000_000_000_000, bytes32([0] * 32), DEFAULT_TX_CONFIG, memos=[ph]
|
|
1294
|
--
)
|
|
1295
|
--
await wallet_node.wallet_state_manager.add_pending_transactions([tx])
|
|
1296
|
--
|
|
1297
|
--
async def tx_in_mempool():
|
|
1298
|
--
return full_node_api.full_node.mempool_manager.get_spendbundle(tx.name) is not None
|
|
1299
|
--
|
|
1300
|
--
await time_out_assert(15, tx_in_mempool)
|
|
1301
|
--
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
|
|
1302
|
--
|
|
1303
|
--
await assert_coin_state_retry()
|
|
1304
|
--
|
|
1305
|
--
assert not wallet_node.coin_state_flaky
|
|
1306
|
--
assert request_puzzle_solution_failure_tested
|
|
1307
|
--
assert not wallet_node.fetch_children_flaky
|
|
1308
|
--
assert not wallet_node.get_timestamp_flaky
|
|
1309
|
--
assert not wallet_node.db_flaky
|
|
1310
|
--
await time_out_assert(30, wallet.get_confirmed_balance, 1_000_000_000_000)
|
|
1311
|
--
|
|
1312
|
--
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
1313
|
--
@pytest.mark.anyio
|
|
1314
|
--
async def test_bad_peak_mismatch(self, two_wallet_nodes, default_1000_blocks, self_hostname, blockchain_constants):
|
|
1315
|
--
full_nodes, wallets, bt = two_wallet_nodes
|
|
1316
|
--
wallet_node, wallet_server = wallets[0]
|
|
1317
|
--
full_node_api = full_nodes[0]
|
|
1318
|
--
full_node_server = full_node_api.full_node.server
|
|
1319
|
--
blocks = default_1000_blocks
|
|
1320
|
--
header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(
|
|
1321
|
--
blocks, blockchain_constants
|
|
1234
|
++
[tx] = await wallet.generate_signed_transaction(
|
|
1235
|
++
1_000_000_000_000, bytes32([0] * 32), DEFAULT_TX_CONFIG, memos=[ph]
|
|
1322
1236
|
)
|
|
1323
|
--
|
|
1237
|
++
await wallet_node.wallet_state_manager.add_pending_transactions([tx])
|
|
1324
1238
|
|
|
1325
|
--
|
|
1326
|
--
|
|
1327
|
--
for block in blocks:
|
|
1328
|
--
await full_node_api.full_node.add_block(block)
|
|
1239
|
++
async def tx_in_mempool():
|
|
1240
|
++
return full_node_api.full_node.mempool_manager.get_spendbundle(tx.name) is not None
|
|
1329
1241
|
|
|
1330
|
--
await
|
|
1242
|
++
await time_out_assert(15, tx_in_mempool)
|
|
1243
|
++
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
|
|
1331
1244
|
|
|
1332
|
--
|
|
1333
|
--
|
|
1334
|
--
|
|
1335
|
--
|
|
1336
|
--
|
|
1337
|
--
|
|
1338
|
--
|
|
1245
|
++
await assert_coin_state_retry()
|
|
1246
|
++
|
|
1247
|
++
assert not wallet_node.coin_state_flaky
|
|
1248
|
++
assert request_puzzle_solution_failure_tested
|
|
1249
|
++
assert not wallet_node.fetch_children_flaky
|
|
1250
|
++
assert not wallet_node.get_timestamp_flaky
|
|
1251
|
++
assert not wallet_node.db_flaky
|
|
1252
|
++
await time_out_assert(30, wallet.get_confirmed_balance, 1_000_000_000_000)
|
|
1253
|
++
|
|
1254
|
++
|
|
1255
|
++
@pytest.mark.limit_consensus_modes(reason="save time")
|
|
1256
|
++
@pytest.mark.anyio
|
|
1257
|
++
async def test_bad_peak_mismatch(
|
|
1258
|
++
two_wallet_nodes, default_1000_blocks, self_hostname, blockchain_constants, monkeypatch
|
|
1259
|
++
):
|
|
1260
|
++
full_nodes, wallets, bt = two_wallet_nodes
|
|
1261
|
++
wallet_node, wallet_server = wallets[0]
|
|
1262
|
++
full_node_api = full_nodes[0]
|
|
1263
|
++
full_node_server = full_node_api.full_node.server
|
|
1264
|
++
blocks = default_1000_blocks
|
|
1265
|
++
header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks, blockchain_constants)
|
|
1266
|
++
wpf = WeightProofHandler(blockchain_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
|
|
1267
|
++
|
|
1268
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
1269
|
++
|
|
1270
|
++
for block in blocks:
|
|
1271
|
++
await full_node_api.full_node.add_block(block)
|
|
1272
|
++
|
|
1273
|
++
await wallet_server.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None)
|
|
1274
|
++
|
|
1275
|
++
# make wp for lower height
|
|
1276
|
++
wp = await wpf.get_proof_of_weight(height_to_hash[800])
|
|
1277
|
++
# create the node respond with the lighter proof
|
|
1278
|
++
wp_msg = make_msg(
|
|
1279
|
++
ProtocolMessageTypes.respond_proof_of_weight,
|
|
1280
|
++
full_node_protocol.RespondProofOfWeight(wp, wp.recent_chain_data[-1].header_hash),
|
|
1281
|
++
)
|
|
1282
|
++
with monkeypatch.context() as m:
|
|
1339
1283
|
f = asyncio.Future()
|
|
1340
1284
|
f.set_result(wp_msg)
|
|
1341
|
--
full_node_api
|
|
1285
|
++
m.setattr(full_node_api, "request_proof_of_weight", MagicMock(return_value=f))
|
|
1342
1286
|
|
|
1343
1287
|
# create the node respond with the lighter header block
|
|
1344
1288
|
header_block_msg = make_msg(
|
|
@@@ -1347,7 -1347,7 +1291,7 @@@
|
|
|
1347
1291
|
)
|
|
1348
1292
|
f2 = asyncio.Future()
|
|
1349
1293
|
f2.set_result(header_block_msg)
|
|
1350
|
--
full_node_api
|
|
1294
|
++
m.setattr(full_node_api, "request_block_header", MagicMock(return_value=f2))
|
|
1351
1295
|
|
|
1352
1296
|
# create new fake peak msg
|
|
1353
1297
|
fake_peak_height = uint32(11000)
|
|
@@@ -6,7 -6,7 +6,7 @@@ from __future__ import annotation
|
|
|
6
6
|
from chia.util.bech32m import bech32_decode
|
|
7
7
|
|
|
8
8
|
|
|
9
|
--
def test_valid_imports():
|
|
9
|
++
def test_valid_imports() -> None:
|
|
10
10
|
test_strings = [
|
|
11
11
|
"A1LQFN3A",
|
|
12
12
|
"a1lqfn3a",
|
|
@@@ -18,11 -18,11 +18,11 @@@
|
|
|
18
18
|
"?1v759aa",
|
|
19
19
|
]
|
|
20
20
|
for test_str in test_strings:
|
|
21
|
--
|
|
21
|
++
_, data = bech32_decode(test_str)
|
|
22
22
|
assert data is not None
|
|
23
23
|
|
|
24
24
|
|
|
25
|
--
def test_invalid_imports():
|
|
25
|
++
def test_invalid_imports() -> None:
|
|
26
26
|
test_strings = [
|
|
27
27
|
f"{0x20}1xj0phk",
|
|
28
28
|
f"{0x7F}1g6xzxy",
|
|
@@@ -41,5 -41,5 +41,5 @@@
|
|
|
41
41
|
"1p2gdwpf",
|
|
42
42
|
]
|
|
43
43
|
for test_str in test_strings:
|
|
44
|
--
|
|
44
|
++
_, data = bech32_decode(test_str)
|
|
45
45
|
assert data is None
|
|
@@@ -42,61 -42,61 +42,60 @@@ class DummyDerivationRecords
|
|
|
42
42
|
self.index_per_wallet[wallet_id] += 1
|
|
43
43
|
|
|
44
44
|
|
|
45
|
--
|
|
46
|
--
|
|
47
|
--
async
|
|
48
|
--
|
|
49
|
--
|
|
50
|
--
|
|
51
|
--
|
|
52
|
--
|
|
53
|
--
|
|
54
|
--
|
|
55
|
--
|
|
56
|
--
|
|
57
|
--
|
|
58
|
--
|
|
59
|
--
False,
|
|
60
|
--
)
|
|
45
|
++
@pytest.mark.anyio
|
|
46
|
++
async def test_puzzle_store(seeded_random: random.Random) -> None:
|
|
47
|
++
async with DBConnection(1) as wrapper:
|
|
48
|
++
db = await WalletPuzzleStore.create(wrapper)
|
|
49
|
++
derivation_recs = []
|
|
50
|
++
for i in range(1000):
|
|
51
|
++
derivation_recs.append(
|
|
52
|
++
DerivationRecord(
|
|
53
|
++
uint32(i),
|
|
54
|
++
bytes32.random(seeded_random),
|
|
55
|
++
AugSchemeMPL.key_gen(bytes32.random(seeded_random)).get_g1(),
|
|
56
|
++
WalletType.STANDARD_WALLET,
|
|
57
|
++
uint32(1),
|
|
58
|
++
False,
|
|
61
59
|
)
|
|
62
|
--
|
|
63
|
--
|
|
64
|
--
|
|
65
|
--
|
|
66
|
--
|
|
67
|
--
|
|
68
|
--
|
|
69
|
--
|
|
70
|
--
|
|
60
|
++
)
|
|
61
|
++
derivation_recs.append(
|
|
62
|
++
DerivationRecord(
|
|
63
|
++
uint32(i),
|
|
64
|
++
bytes32.random(seeded_random),
|
|
65
|
++
AugSchemeMPL.key_gen(bytes32.random(seeded_random)).get_g1(),
|
|
66
|
++
WalletType.CAT,
|
|
67
|
++
uint32(2),
|
|
68
|
++
False,
|
|
71
69
|
)
|
|
72
|
--
assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is False
|
|
73
|
--
assert await db.index_for_pubkey(derivation_recs[0].pubkey) is None
|
|
74
|
--
assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
|
|
75
|
--
assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
|
|
76
|
--
assert len(await db.get_all_puzzle_hashes()) == 0
|
|
77
|
--
assert await db.get_last_derivation_path() is None
|
|
78
|
--
assert await db.get_unused_derivation_path() is None
|
|
79
|
--
assert await db.get_derivation_record(0, 2, False) is None
|
|
80
|
--
|
|
81
|
--
await db.add_derivation_paths(derivation_recs)
|
|
82
|
--
|
|
83
|
--
assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is True
|
|
84
|
--
|
|
85
|
--
assert await db.index_for_pubkey(derivation_recs[4].pubkey) == 2
|
|
86
|
--
assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) == 1
|
|
87
|
--
assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) == WalletIdentifier(
|
|
88
|
--
derivation_recs[2].wallet_id,
|
|
89
|
--
derivation_recs[2].wallet_type,
|
|
90
70
|
)
|
|
91
|
--
|
|
92
|
--
|
|
93
|
--
|
|
94
|
--
|
|
95
|
--
|
|
96
|
--
|
|
97
|
--
|
|
98
|
--
|
|
99
|
--
|
|
71
|
++
assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is False
|
|
72
|
++
assert await db.index_for_pubkey(derivation_recs[0].pubkey) is None
|
|
73
|
++
assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
|
|
74
|
++
assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) is None
|
|
75
|
++
assert len(await db.get_all_puzzle_hashes()) == 0
|
|
76
|
++
assert await db.get_last_derivation_path() is None
|
|
77
|
++
assert await db.get_unused_derivation_path() is None
|
|
78
|
++
assert await db.get_derivation_record(0, 2, False) is None
|
|
79
|
++
|
|
80
|
++
await db.add_derivation_paths(derivation_recs)
|
|
81
|
++
|
|
82
|
++
assert await db.puzzle_hash_exists(derivation_recs[0].puzzle_hash) is True
|
|
83
|
++
|
|
84
|
++
assert await db.index_for_pubkey(derivation_recs[4].pubkey) == 2
|
|
85
|
++
assert await db.index_for_puzzle_hash(derivation_recs[2].puzzle_hash) == 1
|
|
86
|
++
assert await db.get_wallet_identifier_for_puzzle_hash(derivation_recs[2].puzzle_hash) == WalletIdentifier(
|
|
87
|
++
derivation_recs[2].wallet_id,
|
|
88
|
++
derivation_recs[2].wallet_type,
|
|
89
|
++
)
|
|
90
|
++
assert len(await db.get_all_puzzle_hashes()) == 2000
|
|
91
|
++
assert await db.get_last_derivation_path() == 999
|
|
92
|
++
assert await db.get_unused_derivation_path() == 0
|
|
93
|
++
assert await db.get_derivation_record(0, 2, False) == derivation_recs[1]
|
|
94
|
++
|
|
95
|
++
# Indeces up to 250
|
|
96
|
++
await db.set_used_up_to(249)
|
|
97
|
++
|
|
98
|
++
assert await db.get_unused_derivation_path() == 250
|
|
100
99
|
|
|
101
100
|
|
|
102
101
|
@pytest.mark.anyio
|
|
@@@ -38,14 -38,14 +38,14 @@@ def p2_singleton_puzzle_hash(launcher_i
|
|
|
38
38
|
return p2_singleton_puzzle(launcher_id, launcher_puzzle_hash).get_tree_hash()
|
|
39
39
|
|
|
40
40
|
|
|
41
|
--
def test_only_odd_coins():
|
|
41
|
++
def test_only_odd_coins() -> None:
|
|
42
42
|
singleton_mod_hash = SINGLETON_MOD.get_tree_hash()
|
|
43
43
|
# (SINGLETON_STRUCT INNER_PUZZLE lineage_proof my_amount inner_solution)
|
|
44
44
|
# SINGLETON_STRUCT = (MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))
|
|
45
45
|
solution = Program.to(
|
|
46
46
|
[
|
|
47
47
|
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
|
|
48
|
--
Program.to(binutils.assemble("(q (51 0xcafef00d 200))")),
|
|
48
|
++
Program.to(binutils.assemble("(q (51 0xcafef00d 200))")), # type: ignore[no-untyped-call]
|
|
49
49
|
[0xDEADBEEF, 0xCAFEF00D, 200],
|
|
50
50
|
200,
|
|
51
51
|
[],
|
|
@@@ -53,27 -53,27 +53,28 @@@
|
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
with pytest.raises(Exception) as exception_info:
|
|
56
|
--
|
|
56
|
++
SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
|
|
57
57
|
assert exception_info.value.args == ("clvm raise", "80")
|
|
58
58
|
|
|
59
59
|
solution = Program.to(
|
|
60
60
|
[
|
|
61
61
|
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
|
|
62
|
--
Program.to(binutils.assemble("(q (51 0xcafef00d 201))")),
|
|
62
|
++
Program.to(binutils.assemble("(q (51 0xcafef00d 201))")), # type: ignore[no-untyped-call]
|
|
63
63
|
[0xDEADBEEF, 0xCAFED00D, 210],
|
|
64
64
|
205,
|
|
65
65
|
0,
|
|
66
66
|
]
|
|
67
67
|
)
|
|
68
|
--
|
|
68
|
++
SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
|
|
69
69
|
|
|
70
70
|
|
|
71
|
--
def test_only_one_odd_coin_created():
|
|
71
|
++
def test_only_one_odd_coin_created() -> None:
|
|
72
72
|
singleton_mod_hash = SINGLETON_MOD.get_tree_hash()
|
|
73
|
++
clsp = "(q (51 0xcafef00d 203) (51 0xfadeddab 205))"
|
|
73
74
|
solution = Program.to(
|
|
74
75
|
[
|
|
75
76
|
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
|
|
76
|
--
Program.to(binutils.assemble(
|
|
77
|
++
Program.to(binutils.assemble(clsp)), # type: ignore[no-untyped-call]
|
|
77
78
|
[0xDEADBEEF, 0xCAFEF00D, 411],
|
|
78
79
|
411,
|
|
79
80
|
[],
|
|
@@@ -81,22 -81,22 +82,22 @@@
|
|
|
81
82
|
)
|
|
82
83
|
|
|
83
84
|
with pytest.raises(Exception) as exception_info:
|
|
84
|
--
|
|
85
|
++
SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
|
|
85
86
|
assert exception_info.value.args == ("clvm raise", "80")
|
|
86
|
--
|
|
87
|
++
clsp = "(q (51 0xcafef00d 203) (51 0xfadeddab 204) (51 0xdeadbeef 202))"
|
|
87
88
|
solution = Program.to(
|
|
88
89
|
[
|
|
89
90
|
(singleton_mod_hash, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH)),
|
|
90
|
--
Program.to(binutils.assemble(
|
|
91
|
++
Program.to(binutils.assemble(clsp)), # type: ignore[no-untyped-call]
|
|
91
92
|
[0xDEADBEEF, 0xCAFEF00D, 411],
|
|
92
93
|
411,
|
|
93
94
|
[],
|
|
94
95
|
]
|
|
95
96
|
)
|
|
96
|
--
|
|
97
|
++
SINGLETON_MOD.run_with_cost(INFINITE_COST, solution)
|
|
97
98
|
|
|
98
99
|
|
|
99
|
--
def test_p2_singleton():
|
|
100
|
++
def test_p2_singleton() -> None:
|
|
100
101
|
# create a singleton. This should call driver code.
|
|
101
102
|
launcher_id = LAUNCHER_ID
|
|
102
103
|
innerpuz = Program.to(1)
|
|
@@@ -111,11 -111,11 +112,11 @@@
|
|
|
111
112
|
# create a `p2_singleton` puzzle. This should call driver code.
|
|
112
113
|
p2_singleton_full = p2_singleton_puzzle(launcher_id, LAUNCHER_PUZZLE_HASH)
|
|
113
114
|
solution = Program.to([innerpuz.get_tree_hash(), p2_singleton_coin_id])
|
|
114
|
--
|
|
115
|
++
_, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
|
|
115
116
|
conditions = parse_sexp_to_conditions(result)
|
|
116
117
|
|
|
117
118
|
p2_singleton_full = p2_singleton_puzzle(launcher_id, LAUNCHER_PUZZLE_HASH)
|
|
118
119
|
solution = Program.to([innerpuz.get_tree_hash(), p2_singleton_coin_id])
|
|
119
|
--
|
|
120
|
++
_, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
|
|
120
121
|
assert result.first().rest().first().as_atom() == expected_announcement
|
|
121
122
|
assert conditions[0].vars[0] == expected_announcement
|
|
@@@ -47,7 -47,7 +47,7 @@@ def launcher_conditions_and_spend_bundl
|
|
|
47
47
|
initial_singleton_inner_puzzle: Program,
|
|
48
48
|
metadata: List[Tuple[str, str]],
|
|
49
49
|
launcher_puzzle: Program = LAUNCHER_PUZZLE,
|
|
50
|
--
) -> Tuple[
|
|
50
|
++
) -> Tuple[bytes32, List[Program], SpendBundle]:
|
|
51
51
|
launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
|
|
52
52
|
launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount)
|
|
53
53
|
singleton_full_puzzle = SINGLETON_MOD.curry(
|
|
@@@ -74,8 -74,8 +74,7 @@@
|
|
|
74
74
|
launcher_solution = Program.to([singleton_full_puzzle_hash, launcher_amount, metadata])
|
|
75
75
|
coin_spend = make_spend(launcher_coin, launcher_puzzle, launcher_solution)
|
|
76
76
|
spend_bundle = SpendBundle([coin_spend], G2Element())
|
|
77
|
--
|
|
78
|
--
return lineage_proof, launcher_coin.name(), expected_conditions, spend_bundle
|
|
77
|
++
return launcher_coin.name(), expected_conditions, spend_bundle
|
|
79
78
|
|
|
80
79
|
|
|
81
80
|
def singleton_puzzle(launcher_id: Program, launcher_puzzle_hash: bytes32, inner_puzzle: Program) -> Program:
|
|
@@@ -109,7 -109,7 +108,7 @@@ async def test_only_odd_coins_0(bt)
|
|
|
109
108
|
launcher_puzzle = LAUNCHER_PUZZLE
|
|
110
109
|
launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
|
|
111
110
|
initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle(ANYONE_CAN_SPEND_PUZZLE)
|
|
112
|
--
|
|
111
|
++
launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle(
|
|
113
112
|
farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle
|
|
114
113
|
)
|
|
115
114
|
|
|
@@@ -272,12 -272,12 +272,13 @@@ def launcher_conditions_and_spend_bundl
|
|
|
272
272
|
puzzle_db.add_puzzle(launcher_puzzle)
|
|
273
273
|
launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
|
|
274
274
|
launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount)
|
|
275
|
--
|
|
275
|
++
launcher_id = launcher_coin.name()
|
|
276
|
++
singleton_full_puzzle = singleton_puzzle(launcher_id, launcher_puzzle_hash, initial_singleton_inner_puzzle)
|
|
276
277
|
puzzle_db.add_puzzle(singleton_full_puzzle)
|
|
277
278
|
singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash()
|
|
278
279
|
message_program = Program.to([singleton_full_puzzle_hash, launcher_amount, metadata])
|
|
279
280
|
expected_announcement = AssertCoinAnnouncement(
|
|
280
|
--
asserted_id=
|
|
281
|
++
asserted_id=launcher_id, asserted_msg=message_program.get_tree_hash()
|
|
281
282
|
)
|
|
282
283
|
expected_conditions = []
|
|
283
284
|
expected_conditions.append(
|
|
@@@ -301,7 -301,7 +302,7 @@@
|
|
|
301
302
|
)
|
|
302
303
|
coin_spend = make_spend(launcher_coin, SerializedProgram.from_program(launcher_puzzle), solution)
|
|
303
304
|
spend_bundle = SpendBundle([coin_spend], G2Element())
|
|
304
|
--
return
|
|
305
|
++
return launcher_id, expected_conditions, spend_bundle
|
|
305
306
|
|
|
306
307
|
|
|
307
308
|
def singleton_puzzle(launcher_id: bytes32, launcher_puzzle_hash: bytes32, inner_puzzle: Program) -> Program:
|
|
@@@ -440,7 -440,7 +441,7 @@@ def spend_coin_to_singleton
|
|
|
440
441
|
return additions, removals
|
|
441
442
|
|
|
442
443
|
|
|
443
|
--
def find_interesting_singletons(
|
|
444
|
++
def find_interesting_singletons(removals: List[CoinSpend]) -> List[SingletonWallet]:
|
|
444
445
|
singletons = []
|
|
445
446
|
for coin_spend in removals:
|
|
446
447
|
if coin_spend.coin.puzzle_hash == LAUNCHER_PUZZLE_HASH:
|
|
@@@ -462,7 -462,7 +463,7 @@@
|
|
|
462
463
|
return singletons
|
|
463
464
|
|
|
464
465
|
|
|
465
|
--
def filter_p2_singleton(puzzle_db: PuzzleDB,
|
|
466
|
++
def filter_p2_singleton(puzzle_db: PuzzleDB, additions: List[Coin]) -> List[Coin]:
|
|
466
467
|
r = []
|
|
467
468
|
for coin in additions:
|
|
468
469
|
puzzle = puzzle_db.puzzle_for_hash(coin.puzzle_hash)
|
|
@@@ -495,7 -495,7 +496,7 @@@ def test_lifecycle_with_coinstore_as_wa
|
|
|
495
496
|
|
|
496
497
|
assert len(list(coin_store.all_unspent_coins())) == 1
|
|
497
498
|
|
|
498
|
--
new_singletons = find_interesting_singletons(
|
|
499
|
++
new_singletons = find_interesting_singletons(removals)
|
|
499
500
|
interested_singletons.extend(new_singletons)
|
|
500
501
|
|
|
501
502
|
assert len(interested_singletons) == 1
|
|
@@@ -512,7 -512,7 +513,7 @@@
|
|
|
512
513
|
now.seconds += 500
|
|
513
514
|
now.height += 1
|
|
514
515
|
|
|
515
|
--
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB,
|
|
516
|
++
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
|
|
516
517
|
assert p2_singleton_coins == [farmed_coin]
|
|
517
518
|
|
|
518
519
|
assert len(list(coin_store.all_unspent_coins())) == 2
|
|
@@@ -544,7 -544,7 +545,7 @@@
|
|
|
544
545
|
now.seconds += 500
|
|
545
546
|
now.height += 1
|
|
546
547
|
|
|
547
|
--
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB,
|
|
548
|
++
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
|
|
548
549
|
assert p2_singleton_coins == [farmed_coin]
|
|
549
550
|
|
|
550
551
|
assert len(list(coin_store.all_unspent_coins())) == 2
|
|
@@@ -622,7 -622,7 +623,7 @@@
|
|
|
622
623
|
now.seconds += 500
|
|
623
624
|
now.height += 1
|
|
624
625
|
|
|
625
|
--
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB,
|
|
626
|
++
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
|
|
626
627
|
assert p2_singleton_coins == [farmed_coin]
|
|
627
628
|
|
|
628
629
|
assert len(list(coin_store.all_unspent_coins())) == 2
|
|
@@@ -676,7 -676,7 +677,7 @@@
|
|
|
676
677
|
now.seconds += 500
|
|
677
678
|
now.height += 1
|
|
678
679
|
|
|
679
|
--
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB,
|
|
680
|
++
p2_singleton_coins = filter_p2_singleton(PUZZLE_DB, [farmed_coin])
|
|
680
681
|
assert p2_singleton_coins == [farmed_coin]
|
|
681
682
|
|
|
682
683
|
assert len(list(coin_store.all_unspent_coins())) == 3
|
|
@@@ -1,5 -1,5 +1,7 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
++
from typing import Any, Dict, List, Tuple
|
|
4
|
++
|
|
3
5
|
import pytest
|
|
4
6
|
|
|
5
7
|
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
@@@ -9,7 -9,7 +11,9 @@@ from chia.types.blockchain_format.sized
|
|
|
9
11
|
from chia.types.coin_spend import make_spend
|
|
10
12
|
from chia.util.errors import ValidationError
|
|
11
13
|
from chia.util.ints import uint64
|
|
14
|
++
from chia.wallet.lineage_proof import LineageProof, LineageProofField
|
|
12
15
|
from chia.wallet.util.compute_hints import HintedCoin, compute_spend_hints_and_additions
|
|
16
|
++
from chia.wallet.util.merkle_utils import list_to_binary_tree
|
|
13
17
|
from chia.wallet.util.tx_config import (
|
|
14
18
|
DEFAULT_COIN_SELECTION_CONFIG,
|
|
15
19
|
DEFAULT_TX_CONFIG,
|
|
@@@ -92,3 -92,3 +96,86 @@@ def test_tx_config() -> None
|
|
|
92
96
|
assert TXConfigLoader.from_json_dict({}).autofill(
|
|
93
97
|
constants=DEFAULT_CONSTANTS, config={"reuse_public_key_for_change": {"1": True}}, logged_in_fingerprint=1
|
|
94
98
|
).to_json_dict() == {**default_tx_config, "reuse_puzhash": True}
|
|
99
|
++
|
|
100
|
++
|
|
101
|
++
def test_list_to_binary_tree() -> None:
|
|
102
|
++
assert list_to_binary_tree([1]) == 1
|
|
103
|
++
assert list_to_binary_tree([1, 2]) == (1, 2)
|
|
104
|
++
assert list_to_binary_tree([1, 2, 3]) == ((1, 2), 3)
|
|
105
|
++
assert list_to_binary_tree([1, 2, 3, 4]) == ((1, 2), (3, 4))
|
|
106
|
++
assert list_to_binary_tree([1, 2, 3, 4, 5]) == (((1, 2), 3), (4, 5))
|
|
107
|
++
with pytest.raises(ValueError):
|
|
108
|
++
list_to_binary_tree([])
|
|
109
|
++
|
|
110
|
++
|
|
111
|
++
@pytest.mark.parametrize(
|
|
112
|
++
"serializations",
|
|
113
|
++
[
|
|
114
|
++
(tuple(), Program.to(None), []),
|
|
115
|
++
((bytes32([0] * 32),), Program.to([bytes32([0] * 32)]), [LineageProofField.PARENT_NAME]),
|
|
116
|
++
(
|
|
117
|
++
(bytes32([0] * 32), bytes32([0] * 32)),
|
|
118
|
++
Program.to([bytes32([0] * 32), bytes32([0] * 32)]),
|
|
119
|
++
[LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH],
|
|
120
|
++
),
|
|
121
|
++
(
|
|
122
|
++
(bytes32([0] * 32), bytes32([0] * 32), uint64(0)),
|
|
123
|
++
Program.to([bytes32([0] * 32), bytes32([0] * 32), uint64(0)]),
|
|
124
|
++
[LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
|
|
125
|
++
),
|
|
126
|
++
],
|
|
127
|
++
)
|
|
128
|
++
def test_lineage_proof_varargs(serializations: Tuple[Tuple[Any, ...], Program, List[LineageProofField]]) -> None:
|
|
129
|
++
var_args, expected_program, lp_fields = serializations
|
|
130
|
++
assert LineageProof(*var_args).to_program() == expected_program
|
|
131
|
++
assert LineageProof(*var_args) == LineageProof.from_program(expected_program, lp_fields)
|
|
132
|
++
|
|
133
|
++
|
|
134
|
++
@pytest.mark.parametrize(
|
|
135
|
++
"serializations",
|
|
136
|
++
[
|
|
137
|
++
({}, Program.to(None), []),
|
|
138
|
++
({"parent_name": bytes32([0] * 32)}, Program.to([bytes32([0] * 32)]), [LineageProofField.PARENT_NAME]),
|
|
139
|
++
(
|
|
140
|
++
{"parent_name": bytes32([0] * 32), "inner_puzzle_hash": bytes32([0] * 32)},
|
|
141
|
++
Program.to([bytes32([0] * 32), bytes32([0] * 32)]),
|
|
142
|
++
[LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH],
|
|
143
|
++
),
|
|
144
|
++
(
|
|
145
|
++
{"parent_name": bytes32([0] * 32), "inner_puzzle_hash": bytes32([0] * 32), "amount": uint64(0)},
|
|
146
|
++
Program.to([bytes32([0] * 32), bytes32([0] * 32), uint64(0)]),
|
|
147
|
++
[LineageProofField.PARENT_NAME, LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
|
|
148
|
++
),
|
|
149
|
++
(
|
|
150
|
++
{"parent_name": bytes32([0] * 32), "amount": uint64(0)},
|
|
151
|
++
Program.to([bytes32([0] * 32), uint64(0)]),
|
|
152
|
++
[LineageProofField.PARENT_NAME, LineageProofField.AMOUNT],
|
|
153
|
++
),
|
|
154
|
++
(
|
|
155
|
++
{"inner_puzzle_hash": bytes32([0] * 32), "amount": uint64(0)},
|
|
156
|
++
Program.to([bytes32([0] * 32), uint64(0)]),
|
|
157
|
++
[LineageProofField.INNER_PUZZLE_HASH, LineageProofField.AMOUNT],
|
|
158
|
++
),
|
|
159
|
++
({"amount": uint64(0)}, Program.to([uint64(0)]), [LineageProofField.AMOUNT]),
|
|
160
|
++
(
|
|
161
|
++
{"inner_puzzle_hash": bytes32([0] * 32)},
|
|
162
|
++
Program.to([bytes32([0] * 32)]),
|
|
163
|
++
[LineageProofField.INNER_PUZZLE_HASH],
|
|
164
|
++
),
|
|
165
|
++
],
|
|
166
|
++
)
|
|
167
|
++
def test_lineage_proof_kwargs(serializations: Tuple[Dict[str, Any], Program, List[LineageProofField]]) -> None:
|
|
168
|
++
kwargs, expected_program, lp_fields = serializations
|
|
169
|
++
assert LineageProof(**kwargs).to_program() == expected_program
|
|
170
|
++
assert LineageProof(**kwargs) == LineageProof.from_program(expected_program, lp_fields)
|
|
171
|
++
|
|
172
|
++
|
|
173
|
++
def test_lineage_proof_errors() -> None:
|
|
174
|
++
with pytest.raises(ValueError, match="Mismatch"):
|
|
175
|
++
LineageProof.from_program(Program.to([]), [LineageProofField.PARENT_NAME])
|
|
176
|
++
with pytest.raises(StopIteration):
|
|
177
|
++
LineageProof.from_program(Program.to([bytes32([0] * 32)]), [])
|
|
178
|
++
with pytest.raises(ValueError):
|
|
179
|
++
LineageProof.from_program(Program.to([bytes32([1] * 32)]), [LineageProofField.AMOUNT])
|
|
180
|
++
with pytest.raises(ValueError):
|
|
181
|
++
LineageProof.from_program(Program.to([uint64(0)]), [LineageProofField.PARENT_NAME])
|
|
@@@ -1,97 -1,97 +1,116 @@@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
++
from typing import List
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
6
7
|
|
|
7
8
|
from chia.consensus.blockchain import AddBlockResult
|
|
8
9
|
from chia.protocols import full_node_protocol
|
|
9
10
|
from chia.types.blockchain_format.vdf import VDFProof
|
|
10
|
--
from chia.types.
|
|
11
|
++
from chia.types.full_block import FullBlock
|
|
12
|
++
from chia.types.header_block import HeaderBlock
|
|
11
13
|
from chia.util.generator_tools import get_block_header
|
|
14
|
++
from chia.util.ints import uint8, uint32
|
|
12
15
|
from chia.wallet.key_val_store import KeyValStore
|
|
13
16
|
from chia.wallet.wallet_blockchain import WalletBlockchain
|
|
14
17
|
from tests.conftest import ConsensusMode
|
|
15
18
|
from tests.util.db_connection import DBConnection
|
|
19
|
++
from tests.util.setup_nodes import OldSimulatorsAndWallets
|
|
16
20
|
|
|
17
21
|
|
|
18
|
--
|
|
19
|
--
|
|
20
|
--
|
|
21
|
--
|
|
22
|
--
|
|
22
|
++
@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN, ConsensusMode.HARD_FORK_2_0], reason="save time")
|
|
23
|
++
@pytest.mark.anyio
|
|
24
|
++
async def test_wallet_blockchain(
|
|
25
|
++
simulator_and_wallet: OldSimulatorsAndWallets, default_1000_blocks: List[FullBlock]
|
|
26
|
++
) -> None:
|
|
27
|
++
[full_node_api], [(wallet_node, _)], bt = simulator_and_wallet
|
|
23
28
|
|
|
24
|
--
|
|
25
|
--
|
|
29
|
++
for block in default_1000_blocks[:600]:
|
|
30
|
++
await full_node_api.full_node.add_block(block)
|
|
26
31
|
|
|
27
|
--
|
|
28
|
--
|
|
29
|
--
|
|
30
|
--
)
|
|
32
|
++
resp = await full_node_api.request_proof_of_weight(
|
|
33
|
++
full_node_protocol.RequestProofOfWeight(
|
|
34
|
++
uint32(default_1000_blocks[499].height + 1), default_1000_blocks[499].header_hash
|
|
31
35
|
)
|
|
32
|
--
|
|
33
|
--
|
|
34
|
--
|
|
35
|
--
|
|
36
|
++
)
|
|
37
|
++
assert resp is not None
|
|
38
|
++
resp_2 = await full_node_api.request_proof_of_weight(
|
|
39
|
++
full_node_protocol.RequestProofOfWeight(
|
|
40
|
++
uint32(default_1000_blocks[460].height + 1), default_1000_blocks[460].header_hash
|
|
36
41
|
)
|
|
37
|
--
|
|
38
|
--
|
|
39
|
--
|
|
40
|
--
|
|
41
|
--
)
|
|
42
|
++
)
|
|
43
|
++
assert resp_2 is not None
|
|
44
|
++
resp_3 = await full_node_api.request_proof_of_weight(
|
|
45
|
++
full_node_protocol.RequestProofOfWeight(
|
|
46
|
++
uint32(default_1000_blocks[505].height + 1), default_1000_blocks[505].header_hash
|
|
42
47
|
)
|
|
43
|
--
|
|
44
|
--
|
|
45
|
--
|
|
46
|
--
|
|
47
|
--
|
|
48
|
--
|
|
49
|
--
|
|
50
|
--
|
|
51
|
--
|
|
52
|
--
|
|
53
|
--
|
|
54
|
--
|
|
55
|
--
|
|
56
|
--
|
|
57
|
--
|
|
58
|
--
|
|
59
|
--
|
|
60
|
--
|
|
61
|
--
|
|
62
|
--
|
|
63
|
--
|
|
64
|
--
|
|
65
|
--
|
|
66
|
--
|
|
67
|
--
|
|
68
|
--
|
|
69
|
--
|
|
70
|
--
|
|
71
|
--
|
|
72
|
--
|
|
73
|
--
|
|
74
|
--
|
|
75
|
--
|
|
76
|
--
|
|
77
|
--
|
|
78
|
--
|
|
79
|
--
|
|
80
|
--
|
|
81
|
--
|
|
82
|
--
|
|
83
|
--
|
|
84
|
--
|
|
85
|
--
|
|
86
|
--
|
|
87
|
--
|
|
88
|
--
|
|
89
|
--
|
|
90
|
--
|
|
91
|
--
|
|
92
|
--
|
|
93
|
--
|
|
94
|
--
|
|
95
|
--
|
|
96
|
--
|
|
97
|
--
|
|
48
|
++
)
|
|
49
|
++
assert resp_3 is not None
|
|
50
|
++
weight_proof = full_node_protocol.RespondProofOfWeight.from_bytes(resp.data).wp
|
|
51
|
++
assert wallet_node._weight_proof_handler is not None
|
|
52
|
++
records = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof, True)
|
|
53
|
++
weight_proof_short = full_node_protocol.RespondProofOfWeight.from_bytes(resp_2.data).wp
|
|
54
|
++
records_short = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof_short, True)
|
|
55
|
++
weight_proof_long = full_node_protocol.RespondProofOfWeight.from_bytes(resp_3.data).wp
|
|
56
|
++
records_long = await wallet_node._weight_proof_handler.validate_weight_proof(weight_proof_long, True)
|
|
57
|
++
|
|
58
|
++
async with DBConnection(1) as db_wrapper:
|
|
59
|
++
store = await KeyValStore.create(db_wrapper)
|
|
60
|
++
chain = await WalletBlockchain.create(store, bt.constants)
|
|
61
|
++
|
|
62
|
++
assert (await chain.get_peak_block()) is None
|
|
63
|
++
assert chain.get_latest_timestamp() == 0
|
|
64
|
++
|
|
65
|
++
await chain.new_valid_weight_proof(weight_proof, records)
|
|
66
|
++
peak_block = await chain.get_peak_block()
|
|
67
|
++
assert peak_block is not None
|
|
68
|
++
assert peak_block.height == 499
|
|
69
|
++
assert chain.get_latest_timestamp() > 0
|
|
70
|
++
|
|
71
|
++
await chain.new_valid_weight_proof(weight_proof_short, records_short)
|
|
72
|
++
peak_block = await chain.get_peak_block()
|
|
73
|
++
assert peak_block is not None
|
|
74
|
++
assert peak_block.height == 499
|
|
75
|
++
|
|
76
|
++
await chain.new_valid_weight_proof(weight_proof_long, records_long)
|
|
77
|
++
peak_block = await chain.get_peak_block()
|
|
78
|
++
assert peak_block is not None
|
|
79
|
++
assert peak_block.height == 505
|
|
80
|
++
|
|
81
|
++
header_blocks: List[HeaderBlock] = []
|
|
82
|
++
for block in default_1000_blocks:
|
|
83
|
++
header_block = get_block_header(block, [], [])
|
|
84
|
++
header_blocks.append(header_block)
|
|
85
|
++
|
|
86
|
++
res, err = await chain.add_block(header_blocks[50])
|
|
87
|
++
print(res, err)
|
|
88
|
++
assert res == AddBlockResult.DISCONNECTED_BLOCK
|
|
89
|
++
|
|
90
|
++
res, err = await chain.add_block(header_blocks[400])
|
|
91
|
++
print(res, err)
|
|
92
|
++
assert res == AddBlockResult.ALREADY_HAVE_BLOCK
|
|
93
|
++
|
|
94
|
++
res, err = await chain.add_block(header_blocks[507])
|
|
95
|
++
print(res, err)
|
|
96
|
++
assert res == AddBlockResult.DISCONNECTED_BLOCK
|
|
97
|
++
|
|
98
|
++
res, err = await chain.add_block(
|
|
99
|
++
dataclasses.replace(header_blocks[506], challenge_chain_ip_proof=VDFProof(uint8(2), b"123", True))
|
|
100
|
++
)
|
|
101
|
++
assert res == AddBlockResult.INVALID_BLOCK
|
|
102
|
++
|
|
103
|
++
peak_block = await chain.get_peak_block()
|
|
104
|
++
assert peak_block is not None
|
|
105
|
++
assert peak_block.height == 505
|
|
106
|
++
|
|
107
|
++
for header_block in header_blocks[506:]:
|
|
108
|
++
res, err = await chain.add_block(header_block)
|
|
109
|
++
assert res == AddBlockResult.NEW_PEAK
|
|
110
|
++
peak_block = await chain.get_peak_block()
|
|
111
|
++
assert peak_block is not None
|
|
112
|
++
assert peak_block.height == header_block.height
|
|
113
|
++
|
|
114
|
++
peak_block = await chain.get_peak_block()
|
|
115
|
++
assert peak_block is not None
|
|
116
|
++
assert peak_block.height == 999
|
|
@@@ -6,10 -6,10 +6,9 @@@ import sy
|
|
|
6
6
|
import time
|
|
7
7
|
import types
|
|
8
8
|
from pathlib import Path
|
|
9
|
--
from typing import
|
|
9
|
++
from typing import List, Optional
|
|
10
10
|
|
|
11
11
|
import pytest
|
|
12
|
--
from chia_rs import PrivateKey
|
|
13
12
|
|
|
14
13
|
from chia.protocols import wallet_protocol
|
|
15
14
|
from chia.protocols.protocol_message_types import ProtocolMessageTypes
|
|
@@@ -35,12 -35,12 +34,12 @@@ from tests.util.time_out_assert import
|
|
|
35
34
|
|
|
36
35
|
@pytest.mark.anyio
|
|
37
36
|
async def test_get_private_key(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
|
|
38
|
--
root_path
|
|
39
|
--
keychain
|
|
40
|
--
config
|
|
41
|
--
node
|
|
42
|
--
sk
|
|
43
|
--
fingerprint
|
|
37
|
++
root_path = root_path_populated_with_config
|
|
38
|
++
keychain = get_temp_keyring
|
|
39
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
40
|
++
node = WalletNode(config, root_path, test_constants, keychain)
|
|
41
|
++
sk = keychain.add_private_key(generate_mnemonic())
|
|
42
|
++
fingerprint = sk.get_g1().get_fingerprint()
|
|
44
43
|
|
|
45
44
|
key = await node.get_private_key(fingerprint)
|
|
46
45
|
|
|
@@@ -50,12 -50,12 +49,12 @@@
|
|
|
50
49
|
|
|
51
50
|
@pytest.mark.anyio
|
|
52
51
|
async def test_get_private_key_default_key(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
|
|
53
|
--
root_path
|
|
54
|
--
keychain
|
|
55
|
--
config
|
|
56
|
--
node
|
|
57
|
--
sk
|
|
58
|
--
fingerprint
|
|
52
|
++
root_path = root_path_populated_with_config
|
|
53
|
++
keychain = get_temp_keyring
|
|
54
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
55
|
++
node = WalletNode(config, root_path, test_constants, keychain)
|
|
56
|
++
sk = keychain.add_private_key(generate_mnemonic())
|
|
57
|
++
fingerprint = sk.get_g1().get_fingerprint()
|
|
59
58
|
|
|
60
59
|
# Add a couple more keys
|
|
61
60
|
keychain.add_private_key(generate_mnemonic())
|
|
@@@ -73,10 -73,10 +72,10 @@@
|
|
|
73
72
|
async def test_get_private_key_missing_key(
|
|
74
73
|
root_path_populated_with_config: Path, get_temp_keyring: Keychain, fingerprint: Optional[int]
|
|
75
74
|
) -> None:
|
|
76
|
--
root_path
|
|
77
|
--
keychain
|
|
78
|
--
config
|
|
79
|
--
node
|
|
75
|
++
root_path = root_path_populated_with_config
|
|
76
|
++
keychain = get_temp_keyring # empty keyring
|
|
77
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
78
|
++
node = WalletNode(config, root_path, test_constants, keychain)
|
|
80
79
|
|
|
81
80
|
# Keyring is empty, so requesting a key by fingerprint or None should return None
|
|
82
81
|
key = await node.get_private_key(fingerprint)
|
|
@@@ -88,12 -88,12 +87,12 @@@
|
|
|
88
87
|
async def test_get_private_key_missing_key_use_default(
|
|
89
88
|
root_path_populated_with_config: Path, get_temp_keyring: Keychain
|
|
90
89
|
) -> None:
|
|
91
|
--
root_path
|
|
92
|
--
keychain
|
|
93
|
--
config
|
|
94
|
--
node
|
|
95
|
--
sk
|
|
96
|
--
fingerprint
|
|
90
|
++
root_path = root_path_populated_with_config
|
|
91
|
++
keychain = get_temp_keyring
|
|
92
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
93
|
++
node = WalletNode(config, root_path, test_constants, keychain)
|
|
94
|
++
sk = keychain.add_private_key(generate_mnemonic())
|
|
95
|
++
fingerprint = sk.get_g1().get_fingerprint()
|
|
97
96
|
|
|
98
97
|
# Stupid sanity check that the fingerprint we're going to use isn't actually in the keychain
|
|
99
98
|
assert fingerprint != 1234567890
|
|
@@@ -106,12 -106,12 +105,12 @@@
|
|
|
106
105
|
|
|
107
106
|
|
|
108
107
|
def test_log_in(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
|
|
109
|
--
root_path
|
|
110
|
--
keychain
|
|
111
|
--
config
|
|
112
|
--
node
|
|
113
|
--
sk
|
|
114
|
--
fingerprint
|
|
108
|
++
root_path = root_path_populated_with_config
|
|
109
|
++
keychain = get_temp_keyring
|
|
110
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
111
|
++
node = WalletNode(config, root_path, test_constants)
|
|
112
|
++
sk = keychain.add_private_key(generate_mnemonic())
|
|
113
|
++
fingerprint = sk.get_g1().get_fingerprint()
|
|
115
114
|
|
|
116
115
|
node.log_in(sk)
|
|
117
116
|
|
|
@@@ -121,23 -121,23 +120,23 @@@
|
|
|
121
120
|
|
|
122
121
|
|
|
123
122
|
def test_log_in_failure_to_write_last_used_fingerprint(
|
|
124
|
--
root_path_populated_with_config: Path, get_temp_keyring: Keychain, monkeypatch:
|
|
123
|
++
root_path_populated_with_config: Path, get_temp_keyring: Keychain, monkeypatch: pytest.MonkeyPatch
|
|
125
124
|
) -> None:
|
|
126
125
|
called_update_last_used_fingerprint: bool = False
|
|
127
126
|
|
|
128
|
--
def patched_update_last_used_fingerprint(self:
|
|
127
|
++
def patched_update_last_used_fingerprint(self: Self) -> None:
|
|
129
128
|
nonlocal called_update_last_used_fingerprint
|
|
130
129
|
called_update_last_used_fingerprint = True
|
|
131
130
|
raise Exception("Generic write failure")
|
|
132
131
|
|
|
133
132
|
with monkeypatch.context() as m:
|
|
134
133
|
m.setattr(WalletNode, "update_last_used_fingerprint", patched_update_last_used_fingerprint)
|
|
135
|
--
root_path
|
|
136
|
--
keychain
|
|
137
|
--
config
|
|
138
|
--
node
|
|
139
|
--
sk
|
|
140
|
--
fingerprint
|
|
134
|
++
root_path = root_path_populated_with_config
|
|
135
|
++
keychain = get_temp_keyring
|
|
136
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
137
|
++
node = WalletNode(config, root_path, test_constants)
|
|
138
|
++
sk = keychain.add_private_key(generate_mnemonic())
|
|
139
|
++
fingerprint = sk.get_g1().get_fingerprint()
|
|
141
140
|
|
|
142
141
|
# Expect log_in to succeed, even though we can't write the last used fingerprint
|
|
143
142
|
node.log_in(sk)
|
|
@@@ -149,12 -149,12 +148,12 @@@
|
|
|
149
148
|
|
|
150
149
|
|
|
151
150
|
def test_log_out(root_path_populated_with_config: Path, get_temp_keyring: Keychain) -> None:
|
|
152
|
--
root_path
|
|
153
|
--
keychain
|
|
154
|
--
config
|
|
155
|
--
node
|
|
156
|
--
sk
|
|
157
|
--
fingerprint
|
|
151
|
++
root_path = root_path_populated_with_config
|
|
152
|
++
keychain = get_temp_keyring
|
|
153
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
154
|
++
node = WalletNode(config, root_path, test_constants)
|
|
155
|
++
sk = keychain.add_private_key(generate_mnemonic())
|
|
156
|
++
fingerprint = sk.get_g1().get_fingerprint()
|
|
158
157
|
|
|
159
158
|
node.log_in(sk)
|
|
160
159
|
|
|
@@@ -170,32 -170,32 +169,32 @@@
|
|
|
170
169
|
|
|
171
170
|
|
|
172
171
|
def test_get_last_used_fingerprint_path(root_path_populated_with_config: Path) -> None:
|
|
173
|
--
root_path
|
|
174
|
--
config
|
|
175
|
--
node
|
|
176
|
--
path
|
|
172
|
++
root_path = root_path_populated_with_config
|
|
173
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
174
|
++
node = WalletNode(config, root_path, test_constants)
|
|
175
|
++
path = node.get_last_used_fingerprint_path()
|
|
177
176
|
|
|
178
177
|
assert path == root_path / "wallet" / "db" / "last_used_fingerprint"
|
|
179
178
|
|
|
180
179
|
|
|
181
180
|
def test_get_last_used_fingerprint(root_path_populated_with_config: Path) -> None:
|
|
182
|
--
path
|
|
181
|
++
path = root_path_populated_with_config / "wallet" / "db" / "last_used_fingerprint"
|
|
183
182
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
184
183
|
path.write_text("1234567890")
|
|
185
184
|
|
|
186
|
--
root_path
|
|
187
|
--
config
|
|
188
|
--
node
|
|
189
|
--
last_used_fingerprint
|
|
185
|
++
root_path = root_path_populated_with_config
|
|
186
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
187
|
++
node = WalletNode(config, root_path, test_constants)
|
|
188
|
++
last_used_fingerprint = node.get_last_used_fingerprint()
|
|
190
189
|
|
|
191
190
|
assert last_used_fingerprint == 1234567890
|
|
192
191
|
|
|
193
192
|
|
|
194
193
|
def test_get_last_used_fingerprint_file_doesnt_exist(root_path_populated_with_config: Path) -> None:
|
|
195
|
--
root_path
|
|
196
|
--
config
|
|
197
|
--
node
|
|
198
|
--
last_used_fingerprint
|
|
194
|
++
root_path = root_path_populated_with_config
|
|
195
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
196
|
++
node = WalletNode(config, root_path, test_constants)
|
|
197
|
++
last_used_fingerprint = node.get_last_used_fingerprint()
|
|
199
198
|
|
|
200
199
|
assert last_used_fingerprint is None
|
|
201
200
|
|
|
@@@ -204,10 -204,10 +203,10 @@@ def test_get_last_used_fingerprint_file
|
|
|
204
203
|
if sys.platform in ["win32", "cygwin"]:
|
|
205
204
|
pytest.skip("Setting UNIX file permissions doesn't apply to Windows")
|
|
206
205
|
|
|
207
|
--
root_path
|
|
208
|
--
config
|
|
209
|
--
node
|
|
210
|
--
path
|
|
206
|
++
root_path = root_path_populated_with_config
|
|
207
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
208
|
++
node = WalletNode(config, root_path, test_constants)
|
|
209
|
++
path = node.get_last_used_fingerprint_path()
|
|
211
210
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
212
211
|
path.write_text("1234567890")
|
|
213
212
|
|
|
@@@ -216,7 -216,7 +215,7 @@@
|
|
|
216
215
|
# Make the file unreadable
|
|
217
216
|
path.chmod(0o000)
|
|
218
217
|
|
|
219
|
--
last_used_fingerprint
|
|
218
|
++
last_used_fingerprint = node.get_last_used_fingerprint()
|
|
220
219
|
|
|
221
220
|
assert last_used_fingerprint is None
|
|
222
221
|
|
|
@@@ -231,22 -231,22 +230,22 @@@
|
|
|
231
230
|
|
|
232
231
|
|
|
233
232
|
def test_get_last_used_fingerprint_file_cant_read_win32(
|
|
234
|
--
root_path_populated_with_config: Path, monkeypatch:
|
|
233
|
++
root_path_populated_with_config: Path, monkeypatch: pytest.MonkeyPatch
|
|
235
234
|
) -> None:
|
|
236
235
|
if sys.platform not in ["win32", "cygwin"]:
|
|
237
236
|
pytest.skip("Windows-specific test")
|
|
238
237
|
|
|
239
|
--
called_read_text
|
|
238
|
++
called_read_text = False
|
|
240
239
|
|
|
241
|
--
def patched_pathlib_path_read_text(self:
|
|
240
|
++
def patched_pathlib_path_read_text(self: Self) -> str:
|
|
242
241
|
nonlocal called_read_text
|
|
243
242
|
called_read_text = True
|
|
244
243
|
raise PermissionError("Permission denied")
|
|
245
244
|
|
|
246
|
--
root_path
|
|
247
|
--
config
|
|
248
|
--
node
|
|
249
|
--
path
|
|
245
|
++
root_path = root_path_populated_with_config
|
|
246
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
247
|
++
node = WalletNode(config, root_path, test_constants)
|
|
248
|
++
path = node.get_last_used_fingerprint_path()
|
|
250
249
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
251
250
|
path.write_text("1234567890")
|
|
252
251
|
|
|
@@@ -268,10 -268,10 +267,10 @@@
|
|
|
268
267
|
|
|
269
268
|
|
|
270
269
|
def test_get_last_used_fingerprint_file_with_whitespace(root_path_populated_with_config: Path) -> None:
|
|
271
|
--
root_path
|
|
272
|
--
config
|
|
273
|
--
node
|
|
274
|
--
path
|
|
270
|
++
root_path = root_path_populated_with_config
|
|
271
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
272
|
++
node = WalletNode(config, root_path, test_constants)
|
|
273
|
++
path = node.get_last_used_fingerprint_path()
|
|
275
274
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
276
275
|
path.write_text("\n\r\n \t1234567890\r\n\n")
|
|
277
276
|
|
|
@@@ -279,9 -279,9 +278,9 @@@
|
|
|
279
278
|
|
|
280
279
|
|
|
281
280
|
def test_update_last_used_fingerprint_missing_fingerprint(root_path_populated_with_config: Path) -> None:
|
|
282
|
--
root_path
|
|
283
|
--
config
|
|
284
|
--
node
|
|
281
|
++
root_path = root_path_populated_with_config
|
|
282
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
283
|
++
node = WalletNode(config, root_path, test_constants)
|
|
285
284
|
node.logged_in_fingerprint = None
|
|
286
285
|
|
|
287
286
|
with pytest.raises(AssertionError):
|
|
@@@ -289,9 -289,9 +288,9 @@@
|
|
|
289
288
|
|
|
290
289
|
|
|
291
290
|
def test_update_last_used_fingerprint_create_intermediate_dirs(root_path_populated_with_config: Path) -> None:
|
|
292
|
--
root_path
|
|
293
|
--
config
|
|
294
|
--
node
|
|
291
|
++
root_path = root_path_populated_with_config
|
|
292
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
293
|
++
node = WalletNode(config, root_path, test_constants)
|
|
295
294
|
node.logged_in_fingerprint = 9876543210
|
|
296
295
|
path = node.get_last_used_fingerprint_path()
|
|
297
296
|
|
|
@@@ -303,9 -303,9 +302,9 @@@
|
|
|
303
302
|
|
|
304
303
|
|
|
305
304
|
def test_update_last_used_fingerprint(root_path_populated_with_config: Path) -> None:
|
|
306
|
--
root_path
|
|
307
|
--
config
|
|
308
|
--
node
|
|
305
|
++
root_path = root_path_populated_with_config
|
|
306
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
307
|
++
node = WalletNode(config, root_path, test_constants)
|
|
309
308
|
node.logged_in_fingerprint = 9876543210
|
|
310
309
|
path = node.get_last_used_fingerprint_path()
|
|
311
310
|
|
|
@@@ -318,8 -318,8 +317,8 @@@
|
|
|
318
317
|
@pytest.mark.parametrize("testing", [True, False])
|
|
319
318
|
@pytest.mark.parametrize("offset", [0, 550, 650])
|
|
320
319
|
def test_timestamp_in_sync(root_path_populated_with_config: Path, testing: bool, offset: int) -> None:
|
|
321
|
--
root_path
|
|
322
|
--
config
|
|
320
|
++
root_path = root_path_populated_with_config
|
|
321
|
++
config = load_config(root_path, "config.yaml", "wallet")
|
|
323
322
|
wallet_node = WalletNode(config, root_path, test_constants)
|
|
324
323
|
now = time.time()
|
|
325
324
|
wallet_node.config["testing"] = testing
|
|
@@@ -353,8 -353,8 +352,8 @@@ async def test_get_timestamp_for_height
|
|
|
353
352
|
# Clear the cache and add the peak back with a modified timestamp
|
|
354
353
|
cache = wallet_node.get_cache_for_peer(full_node_peer)
|
|
355
354
|
cache.clear_after_height(0)
|
|
356
|
--
modified_foliage_transaction_block =
|
|
357
|
--
|
|
355
|
++
modified_foliage_transaction_block = block_at_peak.foliage_transaction_block.replace(
|
|
356
|
++
timestamp=uint64(timestamp_at_peak + 1)
|
|
358
357
|
)
|
|
359
358
|
modified_peak = dataclasses.replace(peak, foliage_transaction_block=modified_foliage_transaction_block)
|
|
360
359
|
cache.add_to_blocks(modified_peak)
|
|
@@@ -385,7 -385,7 +384,7 @@@
|
|
|
385
384
|
|
|
386
385
|
@pytest.mark.anyio
|
|
387
386
|
async def test_unique_puzzle_hash_subscriptions(simulator_and_wallet: OldSimulatorsAndWallets) -> None:
|
|
388
|
--
_, [(node, _)],
|
|
387
|
++
_, [(node, _)], _ = simulator_and_wallet
|
|
389
388
|
puzzle_hashes = await node.get_puzzle_hashes_to_subscribe()
|
|
390
389
|
assert len(puzzle_hashes) > 1
|
|
391
390
|
assert len(set(puzzle_hashes)) == len(puzzle_hashes)
|
|
@@@ -557,7 -557,7 +556,7 @@@ async def test_transaction_send_cache
|
|
|
557
556
|
await time_out_assert(5, logged_spends_len, 2)
|
|
558
557
|
|
|
559
558
|
# Tell the wallet that we recieved the spend (but failed to process it so it should send again)
|
|
560
|
--
msg
|
|
559
|
++
msg = make_msg(
|
|
561
560
|
ProtocolMessageTypes.transaction_ack,
|
|
562
561
|
wallet_protocol.TransactionAck(tx.name, uint8(MempoolInclusionStatus.FAILED), Err.GENERATOR_RUNTIME_ERROR.name),
|
|
563
562
|
)
|
|
@@@ -8,7 -8,7 +8,7 @@@ from tests.util.db_connection import DB
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@pytest.mark.anyio
|
|
11
|
--
async def test_store():
|
|
11
|
++
async def test_store() -> None:
|
|
12
12
|
async with DBConnection(1) as db_wrapper:
|
|
13
13
|
store = await WalletUserStore.create(db_wrapper)
|
|
14
14
|
await store.init_wallet()
|
|
@@@ -17,6 -17,6 +17,7 @@@
|
|
|
17
17
|
assert (await store.get_last_wallet()).id == i
|
|
18
18
|
wallet = await store.create_wallet("CAT_WALLET", WalletType.CAT, "abc")
|
|
19
19
|
assert wallet.id == i + 1
|
|
20
|
++
assert wallet is not None
|
|
20
21
|
assert wallet.id == 5
|
|
21
22
|
|
|
22
23
|
for i in range(2, 6):
|
|
@@@ -18,7 -18,7 +18,7 @@@ from chia.types.blockchain_format.coin
|
|
|
18
18
|
from chia.types.spend_bundle import SpendBundle
|
|
19
19
|
from chia.util.chia_logging import initialize_logging
|
|
20
20
|
from chia.util.ints import uint32, uint64
|
|
21
|
--
from
|
|
21
|
++
from tests.util.constants import test_constants
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
@contextmanager
|
|
@@@ -38,155 -38,155 +38,14 @@@ and in this way they control whether a
|
|
|
38
38
|
from __future__ import annotations
|
|
39
39
|
|
|
40
40
|
import json
|
|
41
|
--
from dataclasses import dataclass
|
|
42
41
|
from pathlib import Path
|
|
43
|
--
from typing import Dict, List, Tuple
|
|
44
42
|
|
|
45
43
|
import click
|
|
46
44
|
|
|
47
|
--
from chia.consensus.constants import ConsensusConstants
|
|
48
45
|
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
49
|
--
from chia.types.blockchain_format.coin import Coin
|
|
50
|
--
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
|
51
|
--
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
52
|
--
from chia.types.condition_opcodes import ConditionOpcode
|
|
53
|
--
from chia.types.condition_with_args import ConditionWithArgs
|
|
54
|
--
from chia.types.generator_types import BlockGenerator
|
|
55
46
|
from chia.util.config import load_config
|
|
56
47
|
from chia.util.default_root import DEFAULT_ROOT_PATH
|
|
57
|
--
from
|
|
58
|
--
from chia.wallet.cat_wallet.cat_utils import match_cat_puzzle
|
|
59
|
--
from chia.wallet.puzzles.load_clvm import load_serialized_clvm_maybe_recompile
|
|
60
|
--
from chia.wallet.uncurried_puzzle import uncurry_puzzle
|
|
61
|
--
|
|
62
|
--
DESERIALIZE_MOD = load_serialized_clvm_maybe_recompile(
|
|
63
|
--
"chialisp_deserialisation.clsp", package_or_requirement="chia.consensus.puzzles"
|
|
64
|
--
)
|
|
65
|
--
|
|
66
|
--
|
|
67
|
--
@dataclass
|
|
68
|
--
class NPC:
|
|
69
|
--
coin_name: bytes32
|
|
70
|
--
puzzle_hash: bytes32
|
|
71
|
--
conditions: List[Tuple[ConditionOpcode, List[ConditionWithArgs]]]
|
|
72
|
--
|
|
73
|
--
|
|
74
|
--
@dataclass
|
|
75
|
--
class CAT:
|
|
76
|
--
asset_id: str
|
|
77
|
--
memo: str
|
|
78
|
--
npc: NPC
|
|
79
|
--
|
|
80
|
--
def cat_to_dict(self):
|
|
81
|
--
return {"asset_id": self.asset_id, "memo": self.memo, "npc": npc_to_dict(self.npc)}
|
|
82
|
--
|
|
83
|
--
|
|
84
|
--
def condition_with_args_to_dict(condition_with_args: ConditionWithArgs):
|
|
85
|
--
return {
|
|
86
|
--
"condition_opcode": condition_with_args.opcode.name,
|
|
87
|
--
"arguments": [arg.hex() for arg in condition_with_args.vars],
|
|
88
|
--
}
|
|
89
|
--
|
|
90
|
--
|
|
91
|
--
def condition_list_to_dict(condition_list: Tuple[ConditionOpcode, List[ConditionWithArgs]]):
|
|
92
|
--
assert all([condition_list[0] == cwa.opcode for cwa in condition_list[1]])
|
|
93
|
--
return [condition_with_args_to_dict(cwa) for cwa in condition_list[1]]
|
|
94
|
--
|
|
95
|
--
|
|
96
|
--
def npc_to_dict(npc: NPC):
|
|
97
|
--
return {
|
|
98
|
--
"coin_name": npc.coin_name.hex(),
|
|
99
|
--
"conditions": [{"condition_type": c[0].name, "conditions": condition_list_to_dict(c)} for c in npc.conditions],
|
|
100
|
--
"puzzle_hash": npc.puzzle_hash.hex(),
|
|
101
|
--
}
|
|
102
|
--
|
|
103
|
--
|
|
104
|
--
def run_generator(block_generator: BlockGenerator, constants: ConsensusConstants, max_cost: int) -> List[CAT]:
|
|
105
|
--
block_args = [bytes(a) for a in block_generator.generator_refs]
|
|
106
|
--
cost, block_result = block_generator.program.run_with_cost(max_cost, [DESERIALIZE_MOD, block_args])
|
|
107
|
--
|
|
108
|
--
coin_spends = block_result.first()
|
|
109
|
--
|
|
110
|
--
cat_list: List[CAT] = []
|
|
111
|
--
for spend in coin_spends.as_iter():
|
|
112
|
--
parent, puzzle, amount, solution = spend.as_iter()
|
|
113
|
--
args = match_cat_puzzle(uncurry_puzzle(puzzle))
|
|
114
|
--
|
|
115
|
--
if args is None:
|
|
116
|
--
continue
|
|
117
|
--
|
|
118
|
--
_, asset_id, _ = args
|
|
119
|
--
memo = ""
|
|
120
|
--
|
|
121
|
--
puzzle_result = puzzle.run(solution)
|
|
122
|
--
|
|
123
|
--
conds: Dict[ConditionOpcode, List[ConditionWithArgs]] = {}
|
|
124
|
--
|
|
125
|
--
for condition in puzzle_result.as_python():
|
|
126
|
--
op = ConditionOpcode(condition[0])
|
|
127
|
--
|
|
128
|
--
if op not in conds:
|
|
129
|
--
conds[op] = []
|
|
130
|
--
|
|
131
|
--
if condition[0] != ConditionOpcode.CREATE_COIN or len(condition) < 4:
|
|
132
|
--
conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
|
|
133
|
--
continue
|
|
134
|
--
|
|
135
|
--
# If only 3 elements (opcode + 2 args), there is no memo, this is ph, amount
|
|
136
|
--
if type(condition[3]) is not list:
|
|
137
|
--
# If it's not a list, it's not the correct format
|
|
138
|
--
conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]]))
|
|
139
|
--
continue
|
|
140
|
--
|
|
141
|
--
conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]] + [condition[3][0]]))
|
|
142
|
--
|
|
143
|
--
# special retirement address
|
|
144
|
--
if condition[3][0].hex() != "0000000000000000000000000000000000000000000000000000000000000000":
|
|
145
|
--
continue
|
|
146
|
--
|
|
147
|
--
if len(condition[3]) >= 2:
|
|
148
|
--
try:
|
|
149
|
--
memo = condition[3][1].decode("utf-8", errors="strict")
|
|
150
|
--
except UnicodeError:
|
|
151
|
--
pass # ignore this error which should leave memo as empty string
|
|
152
|
--
|
|
153
|
--
# technically there could be more such create_coin ops in the list but our wallet does not
|
|
154
|
--
# so leaving it for the future
|
|
155
|
--
break
|
|
156
|
--
|
|
157
|
--
puzzle_hash = puzzle.get_tree_hash()
|
|
158
|
--
coin = Coin(bytes32(parent.as_atom()), puzzle_hash, amount.as_int())
|
|
159
|
--
cat_list.append(
|
|
160
|
--
CAT(
|
|
161
|
--
asset_id=bytes(asset_id).hex()[2:],
|
|
162
|
--
memo=memo,
|
|
163
|
--
npc=NPC(coin.name(), puzzle_hash, [(op, cond) for op, cond in conds.items()]),
|
|
164
|
--
)
|
|
165
|
--
)
|
|
166
|
--
|
|
167
|
--
return cat_list
|
|
168
|
--
|
|
169
|
--
|
|
170
|
--
def ref_list_to_args(ref_list: List[uint32], root_path: Path) -> List[SerializedProgram]:
|
|
171
|
--
args = []
|
|
172
|
--
for height in ref_list:
|
|
173
|
--
with open(root_path / f"{height}.json", "rb") as f:
|
|
174
|
--
program_str = json.load(f)["block"]["transactions_generator"]
|
|
175
|
--
args.append(SerializedProgram.fromhex(program_str))
|
|
176
|
--
return args
|
|
177
|
--
|
|
178
|
--
|
|
179
|
--
def run_generator_with_args(
|
|
180
|
--
generator_program_hex: str,
|
|
181
|
--
generator_args: List[SerializedProgram],
|
|
182
|
--
constants: ConsensusConstants,
|
|
183
|
--
cost: uint64,
|
|
184
|
--
) -> List[CAT]:
|
|
185
|
--
if not generator_program_hex:
|
|
186
|
--
return []
|
|
187
|
--
generator_program = SerializedProgram.fromhex(generator_program_hex)
|
|
188
|
--
block_generator = BlockGenerator(generator_program, generator_args, [])
|
|
189
|
--
return run_generator(block_generator, constants, min(constants.MAX_BLOCK_COST_CLVM, cost))
|
|
48
|
++
from tests.util.run_block import run_json_block
|
|
190
49
|
|
|
191
50
|
|
|
192
51
|
@click.command()
|
|
@@@ -196,19 -196,19 +55,6 @@@ def cmd_run_json_block_file(filename)
|
|
|
196
55
|
return run_json_block_file(Path(filename))
|
|
197
56
|
|
|
198
57
|
|
|
199
|
--
def run_json_block(full_block, parent: Path, constants: ConsensusConstants) -> List[CAT]:
|
|
200
|
--
ref_list = full_block["block"]["transactions_generator_ref_list"]
|
|
201
|
--
tx_info: dict = full_block["block"]["transactions_info"]
|
|
202
|
--
generator_program_hex: str = full_block["block"]["transactions_generator"]
|
|
203
|
--
cat_list: List[CAT] = []
|
|
204
|
--
if tx_info and generator_program_hex:
|
|
205
|
--
cost = tx_info["cost"]
|
|
206
|
--
args = ref_list_to_args(ref_list, parent)
|
|
207
|
--
cat_list = run_generator_with_args(generator_program_hex, args, constants, cost)
|
|
208
|
--
|
|
209
|
--
return cat_list
|
|
210
|
--
|
|
211
|
--
|
|
212
58
|
def run_json_block_file(filename: Path):
|
|
213
59
|
full_block = json.load(filename.open("rb"))
|
|
214
60
|
# pull in current constants from config.yaml
|
|
@@@ -3,15 -3,15 +3,9 @@@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
--
import cProfile
|
|
7
|
--
import logging
|
|
8
6
|
import os
|
|
9
|
--
import shutil
|
|
10
|
--
import tempfile
|
|
11
|
--
import time
|
|
12
|
--
from contextlib import contextmanager
|
|
13
7
|
from pathlib import Path
|
|
14
|
--
from typing import
|
|
8
|
++
from typing import Optional
|
|
15
9
|
|
|
16
10
|
import aiosqlite
|
|
17
11
|
import click
|
|
@@@ -20,213 -20,213 +14,10 @@@ import zst
|
|
|
20
14
|
from chia.cmds.init_funcs import chia_init
|
|
21
15
|
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
|
22
16
|
from chia.full_node.full_node import FullNode
|
|
23
|
--
from chia.server.outbound_message import Message, NodeType
|
|
24
|
--
from chia.server.server import ChiaServer
|
|
25
17
|
from chia.server.ws_connection import WSChiaConnection
|
|
26
|
--
from chia.simulator.block_tools import make_unfinished_block
|
|
27
|
--
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
28
18
|
from chia.types.full_block import FullBlock
|
|
29
|
--
from chia.types.peer_info import PeerInfo
|
|
30
19
|
from chia.util.config import load_config
|
|
31
|
--
from
|
|
32
|
--
from tools.test_constants import test_constants as TEST_CONSTANTS
|
|
33
|
--
|
|
34
|
--
|
|
35
|
--
class ExitOnError(logging.Handler):
|
|
36
|
--
def __init__(self):
|
|
37
|
--
super().__init__()
|
|
38
|
--
self.exit_with_failure = False
|
|
39
|
--
|
|
40
|
--
def emit(self, record):
|
|
41
|
--
if record.levelno != logging.ERROR:
|
|
42
|
--
return
|
|
43
|
--
self.exit_with_failure = True
|
|
44
|
--
|
|
45
|
--
|
|
46
|
--
@contextmanager
|
|
47
|
--
def enable_profiler(profile: bool, counter: int) -> Iterator[None]:
|
|
48
|
--
if not profile:
|
|
49
|
--
yield
|
|
50
|
--
return
|
|
51
|
--
|
|
52
|
--
with cProfile.Profile() as pr:
|
|
53
|
--
receive_start_time = time.monotonic()
|
|
54
|
--
yield
|
|
55
|
--
|
|
56
|
--
if time.monotonic() - receive_start_time > 5:
|
|
57
|
--
pr.create_stats()
|
|
58
|
--
pr.dump_stats(f"slow-batch-{counter:05d}.profile")
|
|
59
|
--
|
|
60
|
--
|
|
61
|
--
class FakeServer:
|
|
62
|
--
async def send_to_all(self, messages: List[Message], node_type: NodeType, exclude: Optional[bytes32] = None):
|
|
63
|
--
pass
|
|
64
|
--
|
|
65
|
--
def set_received_message_callback(self, callback: Callable):
|
|
66
|
--
pass
|
|
67
|
--
|
|
68
|
--
async def get_peer_info(self) -> Optional[PeerInfo]:
|
|
69
|
--
return None
|
|
70
|
--
|
|
71
|
--
def get_connections(
|
|
72
|
--
self, node_type: Optional[NodeType] = None, *, outbound: Optional[bool] = False
|
|
73
|
--
) -> List[WSChiaConnection]:
|
|
74
|
--
return []
|
|
75
|
--
|
|
76
|
--
def is_duplicate_or_self_connection(self, target_node: PeerInfo) -> bool:
|
|
77
|
--
return False
|
|
78
|
--
|
|
79
|
--
async def start_client(
|
|
80
|
--
self,
|
|
81
|
--
target_node: PeerInfo,
|
|
82
|
--
on_connect: Callable = None,
|
|
83
|
--
auth: bool = False,
|
|
84
|
--
is_feeler: bool = False,
|
|
85
|
--
) -> bool:
|
|
86
|
--
return False
|
|
87
|
--
|
|
88
|
--
|
|
89
|
--
class FakePeer:
|
|
90
|
--
def get_peer_logging(self) -> PeerInfo:
|
|
91
|
--
return PeerInfo("0.0.0.0", uint16(0))
|
|
92
|
--
|
|
93
|
--
def __init__(self):
|
|
94
|
--
self.peer_node_id = bytes([0] * 32)
|
|
95
|
--
|
|
96
|
--
async def get_peer_info(self) -> Optional[PeerInfo]:
|
|
97
|
--
return None
|
|
98
|
--
|
|
99
|
--
|
|
100
|
--
async def run_sync_test(
|
|
101
|
--
file: Path,
|
|
102
|
--
db_version,
|
|
103
|
--
profile: bool,
|
|
104
|
--
single_thread: bool,
|
|
105
|
--
test_constants: bool,
|
|
106
|
--
keep_up: bool,
|
|
107
|
--
db_sync: str,
|
|
108
|
--
node_profiler: bool,
|
|
109
|
--
start_at_checkpoint: Optional[str],
|
|
110
|
--
) -> None:
|
|
111
|
--
logger = logging.getLogger()
|
|
112
|
--
logger.setLevel(logging.WARNING)
|
|
113
|
--
handler = logging.FileHandler("test-full-sync.log")
|
|
114
|
--
handler.setFormatter(
|
|
115
|
--
logging.Formatter(
|
|
116
|
--
"%(levelname)-8s %(message)s",
|
|
117
|
--
datefmt="%Y-%m-%dT%H:%M:%S",
|
|
118
|
--
)
|
|
119
|
--
)
|
|
120
|
--
logger.addHandler(handler)
|
|
121
|
--
check_log = ExitOnError()
|
|
122
|
--
logger.addHandler(check_log)
|
|
123
|
--
|
|
124
|
--
with tempfile.TemporaryDirectory() as root_dir:
|
|
125
|
--
root_path = Path(root_dir, "root")
|
|
126
|
--
if start_at_checkpoint is not None:
|
|
127
|
--
shutil.copytree(start_at_checkpoint, root_path)
|
|
128
|
--
|
|
129
|
--
chia_init(root_path, should_check_keys=False, v1_db=(db_version == 1))
|
|
130
|
--
config = load_config(root_path, "config.yaml")
|
|
131
|
--
|
|
132
|
--
if test_constants:
|
|
133
|
--
constants = TEST_CONSTANTS
|
|
134
|
--
else:
|
|
135
|
--
overrides = config["network_overrides"]["constants"][config["selected_network"]]
|
|
136
|
--
constants = DEFAULT_CONSTANTS.replace_str_to_bytes(**overrides)
|
|
137
|
--
if single_thread:
|
|
138
|
--
config["full_node"]["single_threaded"] = True
|
|
139
|
--
config["full_node"]["db_sync"] = db_sync
|
|
140
|
--
config["full_node"]["enable_profiler"] = node_profiler
|
|
141
|
--
full_node = await FullNode.create(
|
|
142
|
--
config["full_node"],
|
|
143
|
--
root_path=root_path,
|
|
144
|
--
consensus_constants=constants,
|
|
145
|
--
)
|
|
146
|
--
|
|
147
|
--
full_node.set_server(cast(ChiaServer, FakeServer()))
|
|
148
|
--
async with full_node.manage():
|
|
149
|
--
peak = full_node.blockchain.get_peak()
|
|
150
|
--
if peak is not None:
|
|
151
|
--
height = int(peak.height)
|
|
152
|
--
else:
|
|
153
|
--
height = 0
|
|
154
|
--
|
|
155
|
--
peer: WSChiaConnection = cast(WSChiaConnection, FakePeer())
|
|
156
|
--
|
|
157
|
--
print()
|
|
158
|
--
counter = 0
|
|
159
|
--
monotonic = height
|
|
160
|
--
prev_hash = None
|
|
161
|
--
async with aiosqlite.connect(file) as in_db:
|
|
162
|
--
await in_db.execute("pragma query_only")
|
|
163
|
--
rows = await in_db.execute(
|
|
164
|
--
"SELECT header_hash, height, block FROM full_blocks "
|
|
165
|
--
"WHERE height >= ? AND in_main_chain=1 ORDER BY height",
|
|
166
|
--
(height,),
|
|
167
|
--
)
|
|
168
|
--
|
|
169
|
--
block_batch = []
|
|
170
|
--
|
|
171
|
--
start_time = time.monotonic()
|
|
172
|
--
logger.warning(f"starting test {start_time}")
|
|
173
|
--
worst_batch_height = None
|
|
174
|
--
worst_batch_time_per_block = None
|
|
175
|
--
peer_info = peer.get_peer_logging()
|
|
176
|
--
async for r in rows:
|
|
177
|
--
batch_start_time = time.monotonic()
|
|
178
|
--
with enable_profiler(profile, height):
|
|
179
|
--
block = FullBlock.from_bytes(zstd.decompress(r[2]))
|
|
180
|
--
block_batch.append(block)
|
|
181
|
--
|
|
182
|
--
assert block.height == monotonic
|
|
183
|
--
monotonic += 1
|
|
184
|
--
assert prev_hash is None or block.prev_header_hash == prev_hash
|
|
185
|
--
prev_hash = block.header_hash
|
|
186
|
--
|
|
187
|
--
if len(block_batch) < 32:
|
|
188
|
--
continue
|
|
189
|
--
|
|
190
|
--
if keep_up:
|
|
191
|
--
for b in block_batch:
|
|
192
|
--
await full_node.add_unfinished_block(make_unfinished_block(b, constants), peer)
|
|
193
|
--
await full_node.add_block(b)
|
|
194
|
--
else:
|
|
195
|
--
success, summary, _ = await full_node.add_block_batch(block_batch, peer_info, None)
|
|
196
|
--
end_height = block_batch[-1].height
|
|
197
|
--
full_node.blockchain.clean_block_record(end_height - full_node.constants.BLOCKS_CACHE_SIZE)
|
|
198
|
--
|
|
199
|
--
if not success:
|
|
200
|
--
raise RuntimeError("failed to ingest block batch")
|
|
201
|
--
|
|
202
|
--
assert summary is not None
|
|
203
|
--
|
|
204
|
--
time_per_block = (time.monotonic() - batch_start_time) / len(block_batch)
|
|
205
|
--
if not worst_batch_height or worst_batch_time_per_block > time_per_block:
|
|
206
|
--
worst_batch_height = height
|
|
207
|
--
worst_batch_time_per_block = time_per_block
|
|
208
|
--
|
|
209
|
--
counter += len(block_batch)
|
|
210
|
--
height += len(block_batch)
|
|
211
|
--
print(
|
|
212
|
--
f"\rheight {height} {time_per_block:0.2f} s/block ",
|
|
213
|
--
end="",
|
|
214
|
--
)
|
|
215
|
--
block_batch = []
|
|
216
|
--
if check_log.exit_with_failure:
|
|
217
|
--
raise RuntimeError("error printed to log. exiting")
|
|
218
|
--
|
|
219
|
--
if counter >= 100000:
|
|
220
|
--
counter = 0
|
|
221
|
--
print()
|
|
222
|
--
end_time = time.monotonic()
|
|
223
|
--
logger.warning(f"test completed at {end_time}")
|
|
224
|
--
logger.warning(f"duration: {end_time - start_time:0.2f} s")
|
|
225
|
--
logger.warning(f"worst time-per-block: {worst_batch_time_per_block:0.2f} s")
|
|
226
|
--
logger.warning(f"worst height: {worst_batch_height}")
|
|
227
|
--
logger.warning(f"end-height: {height}")
|
|
228
|
--
if node_profiler:
|
|
229
|
--
(root_path / "profile-node").rename("./profile-node")
|
|
20
|
++
from tests.util.full_sync import FakePeer, FakeServer, run_sync_test
|
|
230
21
|
|
|
231
22
|
|
|
232
23
|
@click.group()
|